diff --git a/.gitattributes b/.gitattributes index 58abc747..6bc61b62 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,7 @@ roslib.js linguist-vendored eventemitter2.js linguist-vendored ros3d.js linguist-vendored three.min.js linguist-vendored +json-to-pretty-yaml.js linguist-vendored aruco_pose/vendor/* linguist-vendored blockly/* linguist-vendored highlight/* linguist-vendored diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d7654c4..29b305d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,13 +7,13 @@ on: branches: [ master ] jobs: - melodic: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Native Melodic build - run: | - docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh + # melodic: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - name: Native Melodic build + # run: | + # docker run --rm -v $(pwd):/root/catkin_ws/src/clover ros:melodic-ros-base /root/catkin_ws/src/clover/builder/standalone-install.sh noetic: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cfdd99c7..530aaffb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -38,7 +38,11 @@ jobs: gitbook install gitbook build - name: Generate PDF - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + id: generate-pdf + env: + GITBOOK_SKIP_PDF: ${{ secrets.GITBOOK_SKIP_PDF }} + continue-on-error: ${{ env.GITBOOK_SKIP_PDF != '' }} + if: ${{ github.event_name == 'push' }} run: | for i in 1 2 3 4; do gitbook pdf ./ _book/clover.pdf && break || sleep 1; done sudo apt-get -q install ghostscript @@ -47,6 +51,13 @@ jobs: rm _book/clover_ru.pdf && mv _book/clover_ru_compressed.pdf _book/clover_ru.pdf rm _book/clover_en.pdf && mv _book/clover_en_compressed.pdf _book/clover_en.pdf ls -lah _book/clover*.pdf + echo '::set-output name=GITBOOK_PDF_OK::1' + - name: Download older PDFs + if: ${{ !steps.generate-pdf.outputs.GITBOOK_PDF_OK }} + run: | + rm -f _book/clover*.pdf + wget --no-verbose https://clover.coex.tech/clover_ru.pdf -P _book/ + wget --no-verbose https://clover.coex.tech/clover_en.pdf -P _book/ - name: Deploy uses: JamesIves/github-pages-deploy-action@4.1.3 if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} diff --git a/aruco_pose/CMakeLists.txt b/aruco_pose/CMakeLists.txt index 93b19e76..ce65c428 100644 --- a/aruco_pose/CMakeLists.txt +++ b/aruco_pose/CMakeLists.txt @@ -83,11 +83,10 @@ add_message_files( ) ## Generate services in the 'srv' folder -# add_service_files( -# FILES -# Service1.srv -# Service2.srv -# ) +add_service_files( + FILES + SetMarkers.srv +) ## Generate actions in the 'action' folder # add_action_files( diff --git a/aruco_pose/README.md b/aruco_pose/README.md index 83b43879..d5c88b1f 100644 --- a/aruco_pose/README.md +++ b/aruco_pose/README.md @@ -51,6 +51,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel * `image_raw` (*sensor_msgs/Image*) – camera image * `camera_info` (*sensor_msgs/CameraInfo*) – camera calibration info +* `map_markers` (*aruco_pose/MarkerArray*) – list of markers to disable TF transform publishing #### Published @@ -74,6 +75,7 @@ It's recommended to run it within the same nodelet manager with the camera nodel * `~image_width` – debug image width (default: 2000) * `~image_height` – debug image height (default: 2000) * `~image_margin` – debug image margin (default: 200) +* `~image_axis` – whether debug image should contain axis (default: true) * `~dictionary` (*int*) – ArUco dictionary (default: 2) - should be the same as `dictionary` parameter of `aruco_detect` nodelet Map file has one marker per line with the following line format: @@ -97,6 +99,7 @@ See examples in [`map`](map/) directory. #### Published * `~pose` (*geometry_msgs/PoseWithCovarianceStamped*) – estimated map pose +* `~map` (*aruco_pose/MarkerArray*) – list of markers in the loaded map * `~image` (*sensor_msgs/Image*) – planarized map image * `~visualization` (*visualization_msgs/MarkerArray*) – markers map visualization for rviz * `~debug` (*sensor_msgs/Image*) – debug image with detected markers and map axis diff --git a/aruco_pose/cfg/Detector.cfg b/aruco_pose/cfg/Detector.cfg index 5788e9e9..581bc262 100755 --- a/aruco_pose/cfg/Detector.cfg +++ b/aruco_pose/cfg/Detector.cfg @@ -10,6 +10,8 @@ gen = ParameterGenerator() gen.add("enabled", bool_t, 0, "if detection enabled", True) +gen.add("length", double_t, 0, "markers' side length", min=0, max=10) + gen.add("adaptiveThreshConstant", double_t, 0, "Constant for adaptive thresholding before finding contours", p.adaptiveThreshConstant, 0, 100) diff --git a/aruco_pose/map/cmit.txt b/aruco_pose/map/cmit.txt index dfe273d1..dc368163 100644 --- a/aruco_pose/map/cmit.txt +++ b/aruco_pose/map/cmit.txt @@ -1,3 +1,4 @@ +# id length x y z rot_z rot_y rot_x 0 0.33 0.0 9.0 0 0 0 0 1 0.33 1.0 9.0 0 0 0 0 2 0.33 2.0 9.0 0 0 0 0 diff --git a/aruco_pose/map/office.txt b/aruco_pose/map/office.txt index 9a54274e..d55cb22e 100644 --- a/aruco_pose/map/office.txt +++ b/aruco_pose/map/office.txt @@ -1,3 +1,4 @@ +# id length x y z rot_z rot_y rot_x 107 0.33 0 0 0 0 0 0 106 0.33 0.77 0 0 0 0 0 105 0.33 0 0.77 0 0 0 0 diff --git a/aruco_pose/map/office_ceiling.txt b/aruco_pose/map/office_ceiling.txt index d8c43921..b15c5946 100644 --- a/aruco_pose/map/office_ceiling.txt +++ b/aruco_pose/map/office_ceiling.txt @@ -1,3 +1,4 @@ +# id length x y z rot_z rot_y rot_x 14 0.365 0.000 0.0 0 0 0 0 15 0.365 1.335 0.0 0 0 0 0 30 0.365 2.865 0.0 0 0 0 0 diff --git a/aruco_pose/package.xml b/aruco_pose/package.xml index 43f08abe..03977753 100644 --- a/aruco_pose/package.xml +++ b/aruco_pose/package.xml @@ -1,7 +1,7 @@ aruco_pose - 0.21.1 + 0.23.0 Positioning with ArUco markers Oleg Kalachev diff --git a/aruco_pose/src/aruco_detect.cpp b/aruco_pose/src/aruco_detect.cpp index ae945876..4a6cfac7 100644 --- a/aruco_pose/src/aruco_detect.cpp +++ b/aruco_pose/src/aruco_detect.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include "utils.h" #include @@ -69,8 +70,10 @@ private: image_transport::CameraSubscriber img_sub_; ros::Publisher markers_pub_, vis_markers_pub_; ros::Subscriber map_markers_sub_; + ros::ServiceServer set_markers_srv_; bool estimate_poses_, send_tf_, auto_flip_; double length_; + ros::Duration transform_timeout_; std::unordered_map length_override_; std::string frame_id_prefix_, known_tilt_; Mat camera_matrix_, dist_coeffs_; @@ -97,6 +100,7 @@ public: ros::shutdown(); } readLengthOverride(nh_priv_); + transform_timeout_ = ros::Duration(nh_priv_.param("transform_timeout", 0.02)); known_tilt_ = nh_priv_.param("known_tilt", ""); auto_flip_ = nh_priv_.param("auto_flip", false); @@ -114,6 +118,8 @@ public: dyn_srv_ = std::make_shared>(nh_priv_); dyn_srv_->setCallback(std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2)); + set_markers_srv_ = nh_priv_.advertiseService("set_length_override", &ArucoDetect::setMarkers, this); + debug_pub_ = it_priv.advertise("debug", 1); markers_pub_ = nh_priv_.advertise("markers", 1); vis_markers_pub_ = nh_priv_.advertise("visualization", 1); @@ -172,7 +178,7 @@ private: if (!known_tilt_.empty()) { try { snap_to = tf_buffer_->lookupTransform(msg->header.frame_id, known_tilt_, - msg->header.stamp, ros::Duration(0.02)); + msg->header.stamp, transform_timeout_); } catch (const tf2::TransformException& e) { NODELET_WARN_THROTTLE(5, "can't snap: %s", e.what()); } @@ -346,6 +352,29 @@ private: } } + bool setMarkers(aruco_pose::SetMarkers::Request& req, aruco_pose::SetMarkers::Response& res) + { + for (auto const& marker : req.markers) { + if (marker.id > 999) { + res.message = "Invalid marker id: " + std::to_string(marker.id); + ROS_ERROR("%s", res.message.c_str()); + return true; + } + if (!std::isfinite(marker.length) || marker.length <= 0) { + res.message = "Invalid marker " + std::to_string(marker.id) + " length: " + std::to_string(marker.length); + ROS_ERROR("%s", res.message.c_str()); + return true; + } + } + + for (auto const& marker : req.markers) { + length_override_[marker.id] = marker.length; + } + + res.success = true; + return true; + } + void mapMarkersCallback(const aruco_pose::MarkerArray& msg) { map_markers_ids_.clear(); @@ -356,7 +385,8 @@ private: void paramCallback(aruco_pose::DetectorConfig &config, uint32_t level) { - enabled_ = config.enabled; + enabled_ = config.enabled && config.length > 0; + length_ = config.length; parameters_->adaptiveThreshConstant = config.adaptiveThreshConstant; parameters_->adaptiveThreshWinSizeMin = config.adaptiveThreshWinSizeMin; parameters_->adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMax; diff --git a/aruco_pose/src/aruco_map.cpp b/aruco_pose/src/aruco_map.cpp index b9a24e6c..02f32b0b 100644 --- a/aruco_pose/src/aruco_map.cpp +++ b/aruco_pose/src/aruco_map.cpp @@ -89,7 +89,7 @@ public: // TODO: why image_transport doesn't work here? img_pub_ = nh_priv_.advertise("image", 1, true); - markers_pub_ = nh_priv_.advertise("markers", 1, true); + markers_pub_ = nh_priv_.advertise("map", 1, true); board_ = cv::makePtr(); board_->dictionary = cv::aruco::getPredefinedDictionary( diff --git a/aruco_pose/srv/SetMarkers.srv b/aruco_pose/srv/SetMarkers.srv new file mode 100644 index 00000000..291aba8c --- /dev/null +++ b/aruco_pose/srv/SetMarkers.srv @@ -0,0 +1,7 @@ +# * Add or change markers in the map +# * Change markers' properties, e. g. lengths + +Marker[] markers # if length or pose is nan - remove from map +--- +bool success +string message diff --git a/aruco_pose/test/basic.py b/aruco_pose/test/basic.py index c8eca6c3..94203ef9 100755 --- a/aruco_pose/test/basic.py +++ b/aruco_pose/test/basic.py @@ -143,7 +143,7 @@ def test_map_image(node): assert img.encoding in ('mono8', 'rgb8') def test_map_markers(node): - markers = rospy.wait_for_message('aruco_map/markers', MarkerArray, timeout=5) + markers = rospy.wait_for_message('aruco_map/map', MarkerArray, timeout=5) assert markers.markers[0].id == 1 assert markers.markers[1].id == 2 assert markers.markers[2].id == 3 diff --git a/builder/image-ros.sh b/builder/image-ros.sh index e98934db..afa369b5 100755 --- a/builder/image-ros.sh +++ b/builder/image-ros.sh @@ -90,7 +90,7 @@ 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}-cv-camera=0.5.1-0buster \ ros-${ROS_DISTRO}-image-publisher=1.15.3-0buster \ ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster apt-mark hold \ diff --git a/builder/test/tests.sh b/builder/test/tests.sh index 70616eaa..8eaf9dec 100755 --- a/builder/test/tests.sh +++ b/builder/test/tests.sh @@ -58,5 +58,9 @@ rosversion rosshow rosversion nodelet rosversion image_view +# validate some versions +[[ $(rosversion cv_camera) == "0.5.1" ]] # patched version with init fix +[[ $(rosversion ws281x) == "0.0.13" ]] + # validate examples are present [[ $(ls /home/pi/examples/*) ]] diff --git a/clover/CMakeLists.txt b/clover/CMakeLists.txt index fa64a658..1144902d 100644 --- a/clover/CMakeLists.txt +++ b/clover/CMakeLists.txt @@ -24,6 +24,7 @@ find_package(catkin REQUIRED COMPONENTS tf2_ros image_transport cv_bridge + dynamic_reconfigure ) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") @@ -126,10 +127,9 @@ generate_messages( ## and list every .cfg file to be processed ## Generate dynamic reconfigure parameters in the 'cfg' folder -# generate_dynamic_reconfigure_options( -# cfg/DynReconf1.cfg -# cfg/DynReconf2.cfg -# ) +generate_dynamic_reconfigure_options( + cfg/Flow.cfg +) ################################### ## catkin specific configuration ## @@ -211,6 +211,8 @@ add_dependencies(clover_led ${PROJECT_NAME}_generate_messages_cpp) add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp) +add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_gencfg) + ## Rename C++ executable without prefix ## The above recommended prefix causes long target names, the following renames the ## target back to the shorter version for ease of user use diff --git a/clover/cfg/Flow.cfg b/clover/cfg/Flow.cfg new file mode 100644 index 00000000..21b47682 --- /dev/null +++ b/clover/cfg/Flow.cfg @@ -0,0 +1,10 @@ +#!/usr/bin/env python +PACKAGE = "clover" + +from dynamic_reconfigure.parameter_generator_catkin import * + +gen = ParameterGenerator() + +gen.add("enabled", bool_t, 0, "if optical flow enabled", True) + +exit(gen.generate(PACKAGE, "clover", "Flow")) diff --git a/clover/launch/aruco.launch b/clover/launch/aruco.launch index 821d3fcf..61328f94 100644 --- a/clover/launch/aruco.launch +++ b/clover/launch/aruco.launch @@ -8,16 +8,20 @@ + + + - + - + + @@ -26,7 +30,7 @@ - + @@ -41,11 +45,11 @@ - - + + - - + + diff --git a/clover/launch/clover.launch b/clover/launch/clover.launch index bbf77e8b..58a05f3e 100644 --- a/clover/launch/clover.launch +++ b/clover/launch/clover.launch @@ -12,6 +12,7 @@ + @@ -33,7 +34,10 @@ - + + + + diff --git a/clover/launch/mavros.launch b/clover/launch/mavros.launch index 6db96b6e..33078139 100644 --- a/clover/launch/mavros.launch +++ b/clover/launch/mavros.launch @@ -39,7 +39,7 @@ - + - altitude diff --git a/clover/launch/mavros_config.yaml b/clover/launch/mavros_config.yaml index 29af5352..d6abbf47 100644 --- a/clover/launch/mavros_config.yaml +++ b/clover/launch/mavros_config.yaml @@ -78,6 +78,9 @@ distance_sensor: field_of_view: 0.5 rangefinder_sub: subscriber: true + id: 1 + orientation: PITCH_270 + covariance: 1 # cm # fake_gps fake_gps: diff --git a/clover/package.xml b/clover/package.xml index a4f5878b..8572828a 100644 --- a/clover/package.xml +++ b/clover/package.xml @@ -1,7 +1,7 @@ clover - 0.21.1 + 0.23.0 The Clover package Oleg Kalachev @@ -39,6 +39,7 @@ tf2_web_republisher python-lxml python3-lxml + dynamic_reconfigure python-pymavlink diff --git a/clover/src/autotest/autotest_aruco.py b/clover/src/autotest/autotest_aruco.py new file mode 100755 index 00000000..920eda47 --- /dev/null +++ b/clover/src/autotest/autotest_aruco.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +import rospy +import math +import signal +import sys +import dynamic_reconfigure.client +from clover import srv +from std_srvs.srv import Trigger +from sensor_msgs.msg import Range +from aruco_pose.msg import MarkerArray +from util import handle_response + +rospy.init_node('autotest_aruco', disable_signals=True) # disable signals to allow interrupting with ctrl+c + +flow_client = dynamic_reconfigure.client.Client('optical_flow') +get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry) +navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate)) +land = handle_response(rospy.ServiceProxy('land', Trigger)) + +def interrupt(sig, frame): + print('\nInterrupted, landing...') + land() + sys.exit(0) + +signal.signal(signal.SIGINT, interrupt) + +def print_current_map_position(): + telem = get_telemetry() + dist = rospy.wait_for_message('rangefinder/range', Range).range + print('Map position:\tx={:.1f}\ty={:.1f}\tz={:.1f}\tyaw={:.1f}\tdist={:.2f}'.format(telem.x, telem.y, telem.z, telem.yaw, dist)) + +def navigate_wait(x=0, y=0, z=0, yaw=float('nan'), yaw_rate=0, speed=0.5, \ + frame_id='body', tolerance=0.2, auto_arm=False): + + res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \ + frame_id=frame_id, auto_arm=auto_arm) + + if not res.success: + return res + + while not rospy.is_shutdown(): + telem = get_telemetry(frame_id='navigate_target') + if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance: + return res + rospy.sleep(0.2) + +markers = rospy.wait_for_message('aruco_map/map', MarkerArray, timeout=3) +left = min(marker.pose.position.x for marker in markers.markers) +bottom = min(marker.pose.position.y for marker in markers.markers) +width = max(marker.pose.position.x for marker in markers.markers) +height = max(marker.pose.position.y for marker in markers.markers) +center_x = left + width / 2 +center_y = bottom + height / 2 + +print('Map rect: %g %g - %g %g' % (left, bottom, width, height)) + +input('Take off and hover 1 m [enter] ') +navigate_wait(x=0, y=0, z=1, frame_id='body', auto_arm=True) +print_current_map_position() + +input('Go to corner %g %g 1.5 speed 1 [enter] ' % (width, height)) +navigate_wait(x=width, y=height, z=1.5, speed=1, frame_id='aruco_map') +print_current_map_position() + +input('Go to center %g %g 1.5 speed 5 [enter] ' % (center_x, center_y)) +navigate_wait(x=center_x, y=center_y, z=1.5, speed=5, frame_id='aruco_map') +print_current_map_position() + +input('Disable optical flow and keep hovering [enter] ') +flow_client.update_configuration({'enabled': False}) +rospy.sleep(5) + +input('Enable optical flow back [enter] ') +flow_client.update_configuration({'enabled': True}) + +input('Go to side 1 %g 2 heading top [enter] ' % (center_y)) +navigate_wait(x=1, y=center_y, z=2, yaw=1.57, frame_id='aruco_map') +print_current_map_position() + +marker_id = markers.markers[0].id +input('Go to marker %d z=1.5 [enter] ' % marker_id) +navigate_wait(x=0, y=0, z=1.5, yaw=0, frame_id='aruco_%d' % marker_id) +print_current_map_position() + +input('Perform landing [enter] ') +land() diff --git a/clover/src/autotest/autotest_flight.py b/clover/src/autotest/autotest_flight.py new file mode 100755 index 00000000..19adfdbf --- /dev/null +++ b/clover/src/autotest/autotest_flight.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +import rospy +import math +from math import nan +import signal +import sys +from clover import srv +from std_srvs.srv import Trigger +from sensor_msgs.msg import Range +from util import handle_response + +rospy.init_node('autotest_flight', disable_signals=True) # disable signals to allow interrupting with ctrl+c + +get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry) +navigate = handle_response(rospy.ServiceProxy('navigate', srv.Navigate)) +navigate_global = handle_response(rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)) +set_position = handle_response(rospy.ServiceProxy('set_position', srv.SetPosition)) +set_velocity = handle_response(rospy.ServiceProxy('set_velocity', srv.SetVelocity)) +set_attitude = handle_response(rospy.ServiceProxy('set_attitude', srv.SetAttitude)) +set_rates = handle_response(rospy.ServiceProxy('set_rates', srv.SetRates)) +land = handle_response(rospy.ServiceProxy('land', Trigger)) + +def interrupt(sig, frame): + print('\nInterrupted, landing...') + land() + sys.exit(0) + +signal.signal(signal.SIGINT, interrupt) + +def navigate_wait(x=0, y=0, z=0, yaw=nan, yaw_rate=0, speed=0.5, \ + frame_id='body', tolerance=0.2, auto_arm=False): + + res = navigate(x=x, y=y, z=z, yaw=yaw, yaw_rate=yaw_rate, speed=speed, \ + frame_id=frame_id, auto_arm=auto_arm) + + if not res.success: + return res + + while not rospy.is_shutdown(): + telem = get_telemetry(frame_id='navigate_target') + if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < tolerance: + return res + rospy.sleep(0.2) + +def print_distance(): + dist = rospy.wait_for_message('rangefinder/range', Range).range + print('Distance: {:.2f}'.format(dist)) + +input('Take off and hover 1 m [enter] ') +navigate_wait(z=1, frame_id='body', auto_arm=True) +print_distance() +start = get_telemetry() + +input('Fly forward 2 m [enter] ') +navigate_wait(x=2, frame_id='navigate_target') +print_distance() + +input('Climb 0.5 m [enter] ') +navigate_wait(z=0.5, frame_id='navigate_target') +print_distance() + +input('Rotate left 90° [enter] ') +navigate(yaw=math.pi / 2, frame_id='navigate_target') +rospy.sleep(3) + +input('Use set_velocity to fly forward 2 m speed 1 [enter]') +set_velocity(vx=1, vy=0.0, vz=0, frame_id='body') +rospy.sleep(2) +set_position(frame_id='body') + +input('Rotate right 90° [enter] ') +navigate(yaw=-math.pi / 2, frame_id='navigate_target') +rospy.sleep(3) + +input('Use set_attitude to fly backwards [enter]') +set_attitude(pitch=-0.3, roll=0, yaw=0, thrust=0.5, frame_id='body') +rospy.sleep(0.3) +set_position(frame_id='body') + +input('Use set_attitude to fly right [enter]') +set_attitude(pitch=0, roll=0.3, yaw=0, thrust=0.5, frame_id='body') +rospy.sleep(0.5) +set_position(frame_id='body') + +input('Use set_rates to fly right [enter]') +set_rates(roll_rate=1.2, thrust=0.5) +rospy.sleep(0.4) +set_position(frame_id='body') + +input('Rotate 360° to the right using yaw_rate [enter]') +set_position(x=nan, y=nan, z=nan, frame_id='body', yaw=nan, yaw_rate=-1) +rospy.sleep(2 * math.pi) +set_position(frame_id='body') + +input('Return to start point [enter]') +navigate_wait(x=start.x, y=start.y, z=start.z, yaw=start.yaw, speed=1, frame_id='map') + +input('Land [enter]') +land() diff --git a/clover/src/autotest/autotest_led.py b/clover/src/autotest/autotest_led.py new file mode 100755 index 00000000..f283599f --- /dev/null +++ b/clover/src/autotest/autotest_led.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import rospy +import functools +from clover.srv import SetLEDEffect +from led_msgs.srv import SetLEDs +from led_msgs.msg import LEDStateArray, LEDState +from util import handle_response + +rospy.init_node('autotest_led', disable_signals=True) + +set_leds = handle_response(rospy.ServiceProxy('led/set_leds', SetLEDs)) +set_effect = handle_response(rospy.ServiceProxy('led/set_effect', SetLEDEffect)) + +led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds) +print('LED count =', led_count) + +print('== Testing effects ==') + +input('Fill red [enter] ') +set_effect(r=255, g=0, b=0) + +input('Fill green [enter] ') +set_effect(r=0, g=100, b=0) + +input('Blink white [enter] ') +set_effect(effect='blink', r=255, g=255, b=255) +rospy.sleep(3) + +input('Blink fast violet [enter] ') +set_effect(effect='blink_fast', r=220, g=20, b=250) +rospy.sleep(3) + +input('Fade to blue [enter] ') +set_effect(effect='fade', r=0, g=0, b=255) + +input('Wipe to yellow [enter] ') +set_effect(effect='wipe', r=255, g=255, b=40) + +input('Flash red [enter] ') +set_effect(effect='flash', r=255, g=0, b=0) +rospy.sleep(1) + +input('Rainbow [enter] ') +set_effect(effect='rainbow') +rospy.sleep(4) + +input('Rainbow fill [enter] ') +set_effect(effect='rainbow_fill') +rospy.sleep(4) + +input('Turn off [enter] ') +set_effect() + +print('== Testing low-level control ==') + +input('Fill orange [enter] ') +set_leds(leds=[LEDState(index=i, r=245, g=155, b=0) for i in range(led_count)]) + +input('Fill blue gradient [enter] ') +set_leds(leds=[LEDState(index=i, r=0, g=20, b=int(255 * i / led_count)) for i in range(led_count)]) + +input('Animate green dot [enter] ') +set_effect() +for i in range(led_count): + if i > 0: + set_leds(leds=[LEDState(index=i - 1, r=0, g=0, b=0)]) + set_leds(leds=[LEDState(index=i, r=0, g=255, b=0)]) + rospy.sleep(0.05) + +input('Turn off [enter] ') +set_effect() diff --git a/clover/src/autotest/util.py b/clover/src/autotest/util.py new file mode 100644 index 00000000..46159318 --- /dev/null +++ b/clover/src/autotest/util.py @@ -0,0 +1,11 @@ +import functools + +# decorator to handle response and print error message +def handle_response(fn): + @functools.wraps(fn) + def wrapper(*args, **kwargs): + res = fn(*args, **kwargs) + if not res.success: + print('\033[91mError:\033[0m {}'.format(res.message)) + return res + return wrapper diff --git a/clover/src/led.cpp b/clover/src/led.cpp index 7e43f29c..8d437c05 100644 --- a/clover/src/led.cpp +++ b/clover/src/led.cpp @@ -31,7 +31,6 @@ ros::Time start_time; double blink_rate, blink_fast_rate, flash_delay, fade_period, wipe_period, rainbow_period; double low_battery_threshold; std::vector error_ignore; -bool blink_state; led_msgs::SetLEDs set_leds; led_msgs::LEDStateArray state, start_state; ros::ServiceClient set_leds_srv; @@ -87,9 +86,8 @@ void proceed(const ros::TimerEvent& event) set_leds.request.leds.resize(led_count); if (current_effect.effect == "blink" || current_effect.effect == "blink_fast") { - blink_state = !blink_state; - // toggle all leds - if (blink_state) { + // enable on odd counter + if (counter % 2 != 0) { fill(current_effect.r, current_effect.g, current_effect.b); } else { fill(0, 0, 0); @@ -222,6 +220,7 @@ bool setEffect(clover::SetLEDEffect::Request& req, clover::SetLEDEffect::Respons counter = 0; start_state = state; start_time = ros::Time::now(); + proceed({ .current_real = start_time }); return true; } diff --git a/clover/src/optical_flow.cpp b/clover/src/optical_flow.cpp index 4c66f0de..e244d849 100644 --- a/clover/src/optical_flow.cpp +++ b/clover/src/optical_flow.cpp @@ -22,11 +22,13 @@ #include #include #include +#include #include #include #include #include #include +#include using cv::Mat; @@ -38,6 +40,7 @@ public: {} private: + bool enabled_; ros::Publisher flow_pub_, velo_pub_, shift_pub_; ros::Time prev_stamp_; std::string fcu_frame_id_, local_frame_id_; @@ -54,6 +57,7 @@ private: std::unique_ptr tf_listener_; bool calc_flow_gyro_; float flow_gyro_default_; + std::shared_ptr> dyn_srv_; void onInit() { @@ -83,6 +87,12 @@ private: img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this); + dyn_srv_ = std::make_shared>(nh_priv); + dynamic_reconfigure::Server::CallbackType cb; + + cb = std::bind(&OpticalFlow::paramCallback, this, std::placeholders::_1, std::placeholders::_2); + dyn_srv_->setCallback(cb); + NODELET_INFO("Optical Flow initialized"); } @@ -109,6 +119,8 @@ private: void flow(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cinfo) { + if (!enabled_) return; + parseCameraInfo(cinfo); auto img = cv_bridge::toCvShare(msg, "mono8")->image; @@ -264,6 +276,14 @@ publish_debug: return flow; } + + void paramCallback(clover::FlowConfig &config, uint32_t level) + { + enabled_ = config.enabled; + if (!enabled_) { + prev_ = Mat(); // clear previous frame + } + } }; PLUGINLIB_EXPORT_CLASS(OpticalFlow, nodelet::Nodelet) diff --git a/clover/src/rc.cpp b/clover/src/rc.cpp index 24132ea8..b9900ee4 100644 --- a/clover/src/rc.cpp +++ b/clover/src/rc.cpp @@ -91,7 +91,7 @@ private: void fakeGCSThread() { // Awful workaround for fixing PX4 not sending STATUSTEXTs - // if there is no GCS hearbeats. + // if there is no GCS heartbeats. // TODO: use timer // TODO: remove, when PX4 get this fixed. ros::Publisher mavlink_pub = nh.advertise("mavlink/to", 1); diff --git a/clover/src/selfcheck.py b/clover/src/selfcheck.py index 3093f813..ad539467 100755 --- a/clover/src/selfcheck.py +++ b/clover/src/selfcheck.py @@ -30,6 +30,7 @@ from visualization_msgs.msg import MarkerArray as VisualizationMarkerArray import tf.transformations as t from aruco_pose.msg import MarkerArray from mavros import mavlink +import locale # TODO: check attitude is present @@ -45,6 +46,8 @@ rospy.init_node('selfcheck') os.environ['ROSCONSOLE_FORMAT']='[${severity}]: ${message}' +# use user's locale to convert numbers, etc +locale.setlocale(locale.LC_ALL, '') tf_buffer = tf2_ros.Buffer() tf_listener = tf2_ros.TransformListener(tf_buffer) @@ -195,24 +198,27 @@ def check_fcu(): failure('no connection to the FCU (check wiring)') return + clover_tag = re.compile(r'-cl[oe]ver\.\d+$') + clover_fw = False + # Make sure the console is available to us mavlink_exec('\n') version_str = mavlink_exec('ver all') if version_str == '': info('no version data available from SITL') - r = re.compile(r'^FW (git tag|version): (v?\d\.\d\.\d.*)$') - is_clover_firmware = False - for ver_line in version_str.split('\n'): - match = r.search(ver_line) - if match is not None: - field, version = match.groups() - info('firmware %s: %s' % (field, version)) - if 'clover' in version or 'clever' in version: - is_clover_firmware = True + for line in version_str.split('\n'): + if line.startswith('FW version: '): + info(line[len('FW version: '):]) + elif line.startswith('FW git tag: '): # only Clover's firmware + tag = line[len('FW git tag: '):] + clover_fw = clover_tag.search(tag) + info(tag) + elif line.startswith('HW arch: '): + info(line[len('HW arch: '):]) - if not is_clover_firmware: - failure('not running Clover PX4 firmware, https://clover.coex.tech/firmware') + if not clover_fw: + info('not Clover PX4 firmware, check https://clover.coex.tech/firmware') est = get_param('SYS_MC_EST_GROUP') if est == 1: @@ -330,7 +336,7 @@ def is_process_running(binary, exact=False, full=False): if exact: args.append('-x') # match exactly with the command name if full: - args.append('-f') # use full process name to match + args.append('-f') # use full command line (including arguments) to match args.append(binary) subprocess.check_output(args) return True @@ -528,6 +534,8 @@ def check_global_position(): rospy.wait_for_message('mavros/global_position/global', NavSatFix, timeout=1) except rospy.ROSException: info('no global position') + if get_param('SYS_MC_EST_GROUP') == 2 and (get_param('EKF2_AID_MASK') & (1 << 0)): + failure('enabled GPS fusion may suppress vision position aiding') @check('Optical flow') @@ -635,13 +643,16 @@ def check_cpu_usage(): continue pid, cpu, cmd = process.split('\t') - if cmd.strip() not in WHITELIST and float(cpu) > 30: + if cmd.strip() not in WHITELIST and locale.atof(cpu) > 30: failure('high CPU usage (%s%%) detected: %s (PID %s)', cpu.strip(), cmd.strip(), pid.strip()) @check('clover.service') def check_clover_service(): + if not os.path.exists('/etc/clover_version'): + return # Don't check not on Clover's image + try: output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(), stderr=subprocess.STDOUT).decode() @@ -697,6 +708,10 @@ def check_image(): @check('Preflight status') def check_preflight_status(): + if is_process_running('px4', exact=True): + info('can\'t check in SITL') + return + # Make sure the console is available to us mavlink_exec('\n') cmdr_output = mavlink_exec('commander check') @@ -718,6 +733,10 @@ def check_preflight_status(): @check('Network') def check_network(): + if not os.path.exists('/etc/clover_version'): + # TODO: + return # Don't check not on Clover's image + ros_hostname = os.environ.get('ROS_HOSTNAME', '').strip() if not ros_hostname: @@ -740,6 +759,14 @@ def check_network(): @check('RPi health') def check_rpi_health(): + try: + import shutil + total, used, free = shutil.disk_usage('/') + if free < 1024 * 1024 * 1024: + failure('disk space is less than 1 GB; consider removing logs (~/.ros/log/)') + except Exception as e: + info('could not check the disk free space: %s', str(e)) + # `vcgencmd get_throttled` output codes taken from # https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md#get_throttled # TODO: support more base platforms? diff --git a/clover/src/simple_offboard.cpp b/clover/src/simple_offboard.cpp index 7e7aaaf9..b8f57033 100644 --- a/clover/src/simple_offboard.cpp +++ b/clover/src/simple_offboard.cpp @@ -61,6 +61,7 @@ std::shared_ptr transform_broadcaster; std::shared_ptr static_transform_broadcaster; // Parameters +string mavros; string local_frame; string fcu_frame; ros::Duration transform_timeout; @@ -149,6 +150,9 @@ void handleState(const mavros_msgs::State& s) inline void publishBodyFrame() { if (body.child_frame_id.empty()) return; + if (!body.header.stamp.isZero() && body.header.stamp == local_position.header.stamp) { + return; // avoid TF_REPEATED_DATA warnings + } tf::Quaternion q; q.setRPY(0, 0, tf::getYaw(local_position.pose.orientation)); @@ -690,7 +694,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl // } if (sp_type == POSITION || sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == VELOCITY || sp_type == ATTITUDE) { - // destination point and/or yaw + // destination point and/or attitude PoseStamped ps; ps.header.frame_id = frame_id; ps.header.stamp = stamp; @@ -699,7 +703,12 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl ps.pose.position.z = z; ps.pose.orientation.w = 1.0; // Ensure quaternion is always valid - if (std::isnan(yaw)) { + if (sp_type == ATTITUDE) { + ps.pose.position.x = 0; + ps.pose.position.y = 0; + ps.pose.position.z = 0; + ps.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw); + } else if (std::isnan(yaw)) { setpoint_yaw_type = YAW_RATE; setpoint_yaw_rate = yaw_rate; } else if (std::isinf(yaw) && yaw > 0) { @@ -861,8 +870,9 @@ int main(int argc, char **argv) static_transform_broadcaster = std::make_shared(); // Params - nh.param("mavros/local_position/tf/frame_id", local_frame, "map"); - nh.param("mavros/local_position/tf/child_frame_id", fcu_frame, "base_link"); + nh_priv.param("mavros", mavros, string("mavros")); // for case of using multiple connections + nh.param(mavros + "/local_position/tf/frame_id", local_frame, "map"); + nh.param(mavros + "/local_position/tf/child_frame_id", fcu_frame, "base_link"); nh_priv.param("target_frame", target.child_frame_id, string("navigate_target")); nh_priv.param("setpoint", setpoint.child_frame_id, string("setpoint")); nh_priv.param("auto_release", auto_release, true); @@ -894,25 +904,25 @@ int main(int argc, char **argv) arming_timeout = ros::Duration(nh_priv.param("arming_timeout", 4.0)); // Service clients - arming = nh.serviceClient("mavros/cmd/arming"); - set_mode = nh.serviceClient("mavros/set_mode"); + arming = nh.serviceClient(mavros + "/cmd/arming"); + set_mode = nh.serviceClient(mavros + "/set_mode"); // Telemetry subscribers - auto state_sub = nh.subscribe("mavros/state", 1, &handleState); - auto velocity_sub = nh.subscribe("mavros/local_position/velocity_body", 1, &handleMessage); - auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage); - auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage); - auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage); - auto manual_control_sub = nh.subscribe("mavros/manual_control/control", 1, &handleMessage); - auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition); + auto state_sub = nh.subscribe(mavros + "/state", 1, &handleState); + auto velocity_sub = nh.subscribe(mavros + "/local_position/velocity_body", 1, &handleMessage); + auto global_position_sub = nh.subscribe(mavros + "/global_position/global", 1, &handleMessage); + auto battery_sub = nh.subscribe(mavros + "/battery", 1, &handleMessage); + auto statustext_sub = nh.subscribe(mavros + "/statustext/recv", 1, &handleMessage); + auto manual_control_sub = nh.subscribe(mavros + "/manual_control/control", 1, &handleMessage); + auto local_position_sub = nh.subscribe(mavros + "/local_position/pose", 1, &handleLocalPosition); // Setpoint publishers - position_pub = nh.advertise("mavros/setpoint_position/local", 1); - position_raw_pub = nh.advertise("mavros/setpoint_raw/local", 1); - attitude_pub = nh.advertise("mavros/setpoint_attitude/attitude", 1); - attitude_raw_pub = nh.advertise("mavros/setpoint_raw/attitude", 1); - rates_pub = nh.advertise("mavros/setpoint_attitude/cmd_vel", 1); - thrust_pub = nh.advertise("mavros/setpoint_attitude/thrust", 1); + position_pub = nh.advertise(mavros + "/setpoint_position/local", 1); + position_raw_pub = nh.advertise(mavros + "/setpoint_raw/local", 1); + attitude_pub = nh.advertise(mavros + "/setpoint_attitude/attitude", 1); + attitude_raw_pub = nh.advertise(mavros + "/setpoint_raw/attitude", 1); + rates_pub = nh.advertise(mavros + "/setpoint_attitude/cmd_vel", 1); + thrust_pub = nh.advertise(mavros + "/setpoint_attitude/thrust", 1); // Service servers auto gt_serv = nh.advertiseService("get_telemetry", &getTelemetry); diff --git a/clover/src/vpe_publisher.cpp b/clover/src/vpe_publisher.cpp index 0c032dcd..b28d9f6e 100644 --- a/clover/src/vpe_publisher.cpp +++ b/clover/src/vpe_publisher.cpp @@ -33,14 +33,14 @@ ros::Subscriber local_position_sub; ros::Timer zero_timer; PoseStamped vpe, pose; ros::Time got_local_pos(0); -ros::Duration publish_zero_timout, publish_zero_duration, offset_timeout; +ros::Duration publish_zero_timeout, publish_zero_duration, offset_timeout; TransformStamped offset; void publishZero(const ros::TimerEvent& e) { - if (e.current_real - vpe.header.stamp < publish_zero_timout) return; // have vpe + if (!vpe.header.stamp.isZero() && e.current_real - vpe.header.stamp < publish_zero_timeout) return; // have vpe - if (e.current_real - pose.header.stamp < publish_zero_timout) { // have local position + if (!pose.header.stamp.isZero() && e.current_real - pose.header.stamp < publish_zero_timeout) { // have local position if (got_local_pos.isZero()) { ROS_INFO("got local position"); got_local_pos = e.current_real; @@ -141,11 +141,11 @@ int main(int argc, char **argv) { vpe_pub = nh_priv.advertise("vpe", 1); //vpe_cov_pub = nh_priv_.advertise("pose_cov_pub", 1); - if (nh_priv.param("publish_zero", false)) { + if (nh_priv.param("force_init", false) || nh_priv.param("publish_zero", false)) { // publish_zero is old name // publish zero to initialize the local position zero_timer = nh.createTimer(ros::Duration(0.1), &publishZero); - publish_zero_timout = ros::Duration(nh_priv.param("publish_zero_timout", 5.0)); - publish_zero_duration = ros::Duration(nh_priv.param("publish_zero_duration", 5.0)); + publish_zero_timeout = ros::Duration(nh_priv.param("force_init_timeout", 5.0)); + publish_zero_duration = ros::Duration(nh_priv.param("force_init_duration", 5.0)); local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &localPositionCallback); } diff --git a/clover/test/basic.py b/clover/test/basic.py index 283180ed..5d9d6c29 100755 --- a/clover/test/basic.py +++ b/clover/test/basic.py @@ -33,3 +33,29 @@ def test_web_video_server(node): # Python 3 import urllib.request as urllib urllib.urlopen("http://localhost:8080").read() + +def test_blocks(node): + rospy.wait_for_service('clover_blocks/run', timeout=5) + rospy.wait_for_service('clover_blocks/stop', timeout=5) + rospy.wait_for_service('clover_blocks/load', timeout=5) + rospy.wait_for_service('clover_blocks/store', timeout=5) + + from std_msgs.msg import String + from clover_blocks.srv import Run + + def wait_print(): + try: + wait_print.result = rospy.wait_for_message('clover_blocks/print', String, timeout=5).data + except Exception as e: + wait_print.result = str(e) + + import threading + t = threading.Thread(target=wait_print) + t.start() + rospy.sleep(0.1) + + run = rospy.ServiceProxy('clover_blocks/run', Run) + assert run(code='print("test")').success == True + + t.join() + assert wait_print.result == 'test' diff --git a/clover/test/basic.test b/clover/test/basic.test index 46a923e6..3aac7f10 100755 --- a/clover/test/basic.test +++ b/clover/test/basic.test @@ -23,10 +23,7 @@ - - - - + @@ -38,6 +35,8 @@ startup: { r: 255, g: 255, b: 255 } + + diff --git a/clover_blocks/README.md b/clover_blocks/README.md index 3077085d..f00cb86c 100644 --- a/clover_blocks/README.md +++ b/clover_blocks/README.md @@ -19,7 +19,7 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app ### Services * `~run` ([*clover_blocks/Run*](srv/Run.srv)) – run Blockly-generated program (in Python). -* `~stop` ([*std_srvs/Trigger*](http://docs.ros.org/melodic/api/std_srvs/html/srv/Trigger.html)) – terminate the running program. +* `~stop` ([*std_srvs/Trigger*](http://docs.ros.org/noetic/api/std_srvs/html/srv/Trigger.html)) – terminate the running program. * `~store` ([*clover_blocks/load*](srv/Store.srv)) – store a user program (to `/programs` by default). * `~load` ([*clover_blocks/load*](srv/Load.srv)) – load all the stored programs. @@ -45,11 +45,11 @@ http:///clover_blocks/?navigate_tolerance=0.5&sleep_time=0.1 #### Published -* `~running` ([*std_msgs/Bool*](http://docs.ros.org/melodic/api/std_msgs/html/msg/Bool.html)) – indicates if the program is currently running. -* `~block` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – current executing block (maximum topic rate is limited). -* `~error` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – user program errors and exceptions. +* `~running` ([*std_msgs/Bool*](http://docs.ros.org/noetic/api/std_msgs/html/msg/Bool.html)) – indicates if the program is currently running. +* `~block` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – current executing block (maximum topic rate is limited). +* `~error` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – user program errors and exceptions. * `~prompt` ([*clover_blocks/Prompt*](msg/Prompt.msg)) – user input request (includes random request ID string). This topic is published from the frontend side: -* `~prompt/` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – user input response. +* `~prompt/` ([*std_msgs/String*](http://docs.ros.org/noetic/api/std_msgs/html/msg/String.html)) – user input response. diff --git a/clover_blocks/package.xml b/clover_blocks/package.xml index aa618f86..384d75f9 100644 --- a/clover_blocks/package.xml +++ b/clover_blocks/package.xml @@ -1,7 +1,7 @@ clover_blocks - 0.21.1 + 0.23.0 Blockly programming support for Clover Oleg Kalachev MIT diff --git a/clover_description/package.xml b/clover_description/package.xml index 4de6b9ea..1e5db705 100644 --- a/clover_description/package.xml +++ b/clover_description/package.xml @@ -1,6 +1,6 @@ clover_description - 0.21.1 + 0.23.0 The clover_description package provides URDF models of the Clover series of quadcopters. Alexey Rogachevskiy diff --git a/clover_description/urdf/clover/clover4_physics.xacro b/clover_description/urdf/clover/clover4_physics.xacro index d297a78c..8fb6d305 100644 --- a/clover_description/urdf/clover/clover4_physics.xacro +++ b/clover_description/urdf/clover/clover4_physics.xacro @@ -35,7 +35,7 @@ - + diff --git a/clover_description/urdf/leds/led_strip.xacro b/clover_description/urdf/leds/led_strip.xacro index 6734cb68..f7c0fb36 100644 --- a/clover_description/urdf/leds/led_strip.xacro +++ b/clover_description/urdf/leds/led_strip.xacro @@ -64,6 +64,12 @@ + + + + ${led_count} + + diff --git a/clover_simulation/CMakeLists.txt b/clover_simulation/CMakeLists.txt index a4a6a2b0..3a575800 100644 --- a/clover_simulation/CMakeLists.txt +++ b/clover_simulation/CMakeLists.txt @@ -37,6 +37,14 @@ target_compile_options(sim_leds PRIVATE -std=c++11) add_dependencies(sim_leds ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) +add_library(sim_leds_controller src/sim_leds_controller.cpp) + +target_include_directories(sim_leds_controller PRIVATE ${catkin_INCLUDE_DIRS} ${GAZEBO_INCLUDE_DIRS}) +target_link_libraries(sim_leds_controller PRIVATE ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES}) +target_compile_options(sim_leds_controller PRIVATE -std=c++11) + +add_dependencies(sim_leds_controller ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + # Gazebo throttling camera plugin # for some reason, CMake does not support per-target link directories, and Gazebo does not put # CameraPlugin into ${GAZEBO_LIBRARIES} diff --git a/clover_simulation/airframes/4500_clover b/clover_simulation/airframes/4500_clover index 655d2699..a68f5d8b 100644 --- a/clover_simulation/airframes/4500_clover +++ b/clover_simulation/airframes/4500_clover @@ -7,30 +7,30 @@ . ${R}etc/init.d-posix/airframes/10016_iris # base on iris -param set ATT_W_EXT_HDG 0.5 -param set ATT_EXT_HDG_M 1 +param set-default ATT_W_EXT_HDG 0.5 +param set-default ATT_EXT_HDG_M 1 # 0 = none, 1 = vision, 2 = mocap -param set COM_DISARM_LAND 1.0 +param set-default COM_DISARM_LAND 1.0 -param set LPE_FLW_SCALE 1.0 -param set LPE_FLW_R 0.2 -param set LPE_FLW_RR 0.0 -param set LPE_FLW_QMIN 10 -param set LPE_VIS_DELAY 0.0 -param set LPE_VIS_Z 0.1 -param set LPE_FUSION 86 +param set-default LPE_FLW_SCALE 1.0 +param set-default LPE_FLW_R 0.2 +param set-default LPE_FLW_RR 0.0 +param set-default LPE_FLW_QMIN 10 +param set-default LPE_VIS_DELAY 0.0 +param set-default LPE_VIS_Z 0.1 +param set-default LPE_FUSION 86 # flow + vis + land detector + gyro comp -param set SENS_FLOW_ROT 0 -param set SENS_FLOW_MINHGT 0.01 -param set SENS_FLOW_MAXHGT 4.0 -param set SENS_FLOW_MAXR 10.0 +param set-default SENS_FLOW_ROT 0 +param set-default SENS_FLOW_MINHGT 0.0 +param set-default SENS_FLOW_MAXHGT 4.0 +param set-default SENS_FLOW_MAXR 10.0 -param set EKF2_AID_MASK 27 # gps + flow + vis pos + vis yaw -param set EKF2_OF_DELAY 0 -param set EKF2_OF_QMIN 10 -param set EKF2_OF_N_MIN 0.05 -param set EKF2_OF_N_MAX 0.2 -param set EKF2_HGT_MODE 2 -param set EKF2_EVA_NOISE 0.1 -param set EKF2_EVP_NOISE 0.1 -param set EKF2_EV_DELAY 0 +param set-default EKF2_AID_MASK 26 # flow + vis pos + vis yaw +param set-default EKF2_OF_DELAY 0 +param set-default EKF2_OF_QMIN 10 +param set-default EKF2_OF_N_MIN 0.05 +param set-default EKF2_OF_N_MAX 0.2 +param set-default EKF2_HGT_MODE 2 # 0 = baro, 1 = gps, 2 = range, 3 = vision +param set-default EKF2_EVA_NOISE 0.1 +param set-default EKF2_EVP_NOISE 0.1 +param set-default EKF2_EV_DELAY 0 diff --git a/clover_simulation/package.xml b/clover_simulation/package.xml index 8771151b..a7d27462 100644 --- a/clover_simulation/package.xml +++ b/clover_simulation/package.xml @@ -1,6 +1,6 @@ clover_simulation - 0.21.1 + 0.23.0 The clover_simulation package provides worlds and launch files for Gazebo. Oleg Kalachev diff --git a/clover_simulation/src/sim_leds.cpp b/clover_simulation/src/sim_leds.cpp index 4dfa7123..fb188af7 100644 --- a/clover_simulation/src/sim_leds.cpp +++ b/clover_simulation/src/sim_leds.cpp @@ -49,14 +49,9 @@ private: std::unique_ptr nh; - ros::ServiceServer setLedsSrv; - // Note: LED state should only be published by the /gazebo node - led_msgs::LEDStateArray ledState; - ros::Publisher statePublisher; // LED state will be read from the topic to avoid creating more services ros::Subscriber stateSubscriber; - bool setLeds(led_msgs::SetLEDs::Request& req, led_msgs::SetLEDs::Response& resp); void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds); public: @@ -73,16 +68,8 @@ public: ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s)", role == Role::Client ? "client" : "server"); nh.reset(new ros::NodeHandle(robotNamespace)); - if (role == Role::Server) - { - setLedsSrv = nh->advertiseService("led/set_leds", &LedController::setLeds, this); - statePublisher = nh->advertise("led/state", 1, true); - } - else - { - // LED state should be published to the "led/state" topic, so we grab our data there - stateSubscriber = nh->subscribe("led/state", 1, &LedController::handleLedsMsg, this); - } + + stateSubscriber = nh->subscribe("led/state", 1, &LedController::handleLedsMsg, this); }; ~LedController() @@ -96,13 +83,9 @@ public: std::lock_guard lock(registryMutex); if (totalLeds > 0) { registeredLeds.resize(totalLeds); - ledState.leds.resize(totalLeds); } ROS_DEBUG_NAMED(("LedController_" + robotNamespace).c_str(), "Registering LED visual plugin to %s (LED id=%d)", (role == Role::Client) ? "client" : "server", ledIdx); registeredLeds[ledIdx] = plugin; - ledState.leds[ledIdx].index = ledIdx; - if (role == Role::Server) - statePublisher.publish(ledState); } void unregisterPlugin(sim_led::LedVisualPlugin* plugin) @@ -157,7 +140,8 @@ public: { auto indexStr = parentName.substr(lastDashPos + 1); try { - myIndex = std::stoi(indexStr); + if (indexStr == "visual") myIndex = 0; // the first visual doesn't have index + else myIndex = std::stoi(indexStr); } catch(const std::exception &e) { gzwarn << "Failed to convert " << indexStr << " to integer: " << e.what() << ", assuming 0\n"; myIndex = 0; @@ -195,26 +179,6 @@ public: }; } -// FIXME: These two functions basically do the same thing, maybe they can be merged? -bool led_controller::LedController::setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp) -{ - std::lock_guard lock(registryMutex); - for(const auto& led : req.leds) - { - if (led.index < registeredLeds.size()) { - auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f); - auto ledPlugin = registeredLeds[led.index]; - if (ledPlugin) ledPlugin->SetColor(color); - ledState.leds[led.index].r = led.r; - ledState.leds[led.index].g = led.g; - ledState.leds[led.index].b = led.b; - } - } - statePublisher.publish(ledState); - resp.success = true; - return true; -} - void led_controller::LedController::handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds) { std::lock_guard lock(registryMutex); diff --git a/clover_simulation/src/sim_leds_controller.cpp b/clover_simulation/src/sim_leds_controller.cpp new file mode 100644 index 00000000..90a9799f --- /dev/null +++ b/clover_simulation/src/sim_leds_controller.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include +#include +#include + +class LedControllerPlugin : public gazebo::ModelPlugin { +private: + std::unique_ptr nh; + std::string ns; + ros::ServiceServer setLedsSrv; + led_msgs::LEDStateArray ledState; + ros::Publisher statePublisher; + std::mutex handleMutex; + +public: + bool setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp) + { + std::lock_guard lock(handleMutex); + for(const auto& led : req.leds) + { + if (led.index < ledState.leds.size()) { + ledState.leds[led.index].r = led.r; + ledState.leds[led.index].g = led.g; + ledState.leds[led.index].b = led.b; + } + } + statePublisher.publish(ledState); + resp.success = true; + return true; + } + + virtual void Load(gazebo::physics::ModelPtr model, sdf::ElementPtr sdf) override + { + ROS_INFO("Initialize LED Controller"); + + // We need "libgazebo_ros_api.so" to be loaded + if (!ros::isInitialized()) + { + ROS_FATAL_NAMED("LedController", "Tried to load ROS plugin when ROS Gazebo API is not loaded. Please use gazebo_ros node to" + "launch Gazebo."); + } + + ns = ""; + + if (sdf->HasElement("robotNamespace")) { + ns = sdf->Get("robotNamespace"); + } + if (!sdf->HasElement("ledCount")) { + gzerr << "ledCount is not set, but is required for the plugin to function correctly\n"; + return; + } + int totalLeds = sdf->Get("ledCount"); + ledState.leds.resize(totalLeds); + for (int i = 0; i < totalLeds; i++) { + ledState.leds[i].index = i; + } + + nh.reset(new ros::NodeHandle(ns)); + + setLedsSrv = nh->advertiseService("led/set_leds", &LedControllerPlugin::setLeds, this); + statePublisher = nh->advertise("led/state", 1, true); + + statePublisher.publish(ledState); + } +}; + +GZ_REGISTER_MODEL_PLUGIN(LedControllerPlugin); diff --git a/docs/assets/c305.jpg b/docs/assets/c305.jpg new file mode 100644 index 00000000..4ca8bf1f Binary files /dev/null and b/docs/assets/c305.jpg differ diff --git a/docs/assets/c4s/antennac4s.jpg b/docs/assets/c4s/antennac4s.jpg new file mode 100644 index 00000000..447289ea Binary files /dev/null and b/docs/assets/c4s/antennac4s.jpg differ diff --git a/docs/assets/c4s/arecibo_broken_c4s.jpg b/docs/assets/c4s/arecibo_broken_c4s.jpg new file mode 100644 index 00000000..70177538 Binary files /dev/null and b/docs/assets/c4s/arecibo_broken_c4s.jpg differ diff --git a/docs/assets/c4s/arecibo_c4s.jpg b/docs/assets/c4s/arecibo_c4s.jpg new file mode 100644 index 00000000..03241424 Binary files /dev/null and b/docs/assets/c4s/arecibo_c4s.jpg differ diff --git a/docs/assets/c4s/code_c4s.png b/docs/assets/c4s/code_c4s.png new file mode 100644 index 00000000..e7a93c6d Binary files /dev/null and b/docs/assets/c4s/code_c4s.png differ diff --git a/docs/assets/c4s/drone_c4s.jpg b/docs/assets/c4s/drone_c4s.jpg new file mode 100644 index 00000000..70b61710 Binary files /dev/null and b/docs/assets/c4s/drone_c4s.jpg differ diff --git a/docs/assets/c4s/logo_c4s.jpg b/docs/assets/c4s/logo_c4s.jpg new file mode 100644 index 00000000..47108745 Binary files /dev/null and b/docs/assets/c4s/logo_c4s.jpg differ diff --git a/docs/assets/c4s/module2_c4s.jpg b/docs/assets/c4s/module2_c4s.jpg new file mode 100644 index 00000000..853af0da Binary files /dev/null and b/docs/assets/c4s/module2_c4s.jpg differ diff --git a/docs/assets/c4s/module_c4s.jpg b/docs/assets/c4s/module_c4s.jpg new file mode 100644 index 00000000..2e631c2f Binary files /dev/null and b/docs/assets/c4s/module_c4s.jpg differ diff --git a/docs/assets/c4s/pic1_c4s.jpg b/docs/assets/c4s/pic1_c4s.jpg new file mode 100644 index 00000000..ab2efdc4 Binary files /dev/null and b/docs/assets/c4s/pic1_c4s.jpg differ diff --git a/docs/assets/c4s/pic2_c4s.jpg b/docs/assets/c4s/pic2_c4s.jpg new file mode 100644 index 00000000..6fd60bf0 Binary files /dev/null and b/docs/assets/c4s/pic2_c4s.jpg differ diff --git a/docs/assets/c4s/pic3_c4s.jpg b/docs/assets/c4s/pic3_c4s.jpg new file mode 100644 index 00000000..57058a47 Binary files /dev/null and b/docs/assets/c4s/pic3_c4s.jpg differ diff --git a/docs/assets/c4s/pic4_c4s.jpg b/docs/assets/c4s/pic4_c4s.jpg new file mode 100644 index 00000000..e535257e Binary files /dev/null and b/docs/assets/c4s/pic4_c4s.jpg differ diff --git a/docs/assets/c4s/shot_c4s.jpg b/docs/assets/c4s/shot_c4s.jpg new file mode 100644 index 00000000..c7386b6d Binary files /dev/null and b/docs/assets/c4s/shot_c4s.jpg differ diff --git a/docs/assets/c4s/string_c4s.jpg b/docs/assets/c4s/string_c4s.jpg new file mode 100644 index 00000000..2223a70d Binary files /dev/null and b/docs/assets/c4s/string_c4s.jpg differ diff --git a/docs/assets/c4s/white_noise_c4s.jpg b/docs/assets/c4s/white_noise_c4s.jpg new file mode 100644 index 00000000..4d086196 Binary files /dev/null and b/docs/assets/c4s/white_noise_c4s.jpg differ diff --git a/docs/assets/clover-rescue-team/allsettings.png b/docs/assets/clover-rescue-team/allsettings.png new file mode 100644 index 00000000..117f52e1 Binary files /dev/null and b/docs/assets/clover-rescue-team/allsettings.png differ diff --git a/docs/assets/clover-rescue-team/bot1.jpg b/docs/assets/clover-rescue-team/bot1.jpg new file mode 100644 index 00000000..a22aad57 Binary files /dev/null and b/docs/assets/clover-rescue-team/bot1.jpg differ diff --git a/docs/assets/clover-rescue-team/bot2.png b/docs/assets/clover-rescue-team/bot2.png new file mode 100644 index 00000000..3c015de0 Binary files /dev/null and b/docs/assets/clover-rescue-team/bot2.png differ diff --git a/docs/assets/clover-rescue-team/bot3.jpg b/docs/assets/clover-rescue-team/bot3.jpg new file mode 100644 index 00000000..6ee19191 Binary files /dev/null and b/docs/assets/clover-rescue-team/bot3.jpg differ diff --git a/docs/assets/clover-rescue-team/bot4.png b/docs/assets/clover-rescue-team/bot4.png new file mode 100644 index 00000000..fd2b3a75 Binary files /dev/null and b/docs/assets/clover-rescue-team/bot4.png differ diff --git a/docs/assets/clover-rescue-team/bot5.jpg b/docs/assets/clover-rescue-team/bot5.jpg new file mode 100644 index 00000000..186eb8ab Binary files /dev/null and b/docs/assets/clover-rescue-team/bot5.jpg differ diff --git a/docs/assets/clover-rescue-team/bot6.jpg b/docs/assets/clover-rescue-team/bot6.jpg new file mode 100644 index 00000000..8b68a814 Binary files /dev/null and b/docs/assets/clover-rescue-team/bot6.jpg differ diff --git a/docs/assets/clover-rescue-team/bot7.jpg b/docs/assets/clover-rescue-team/bot7.jpg new file mode 100644 index 00000000..56559036 Binary files /dev/null and b/docs/assets/clover-rescue-team/bot7.jpg differ diff --git a/docs/assets/clover-rescue-team/m1.jpg b/docs/assets/clover-rescue-team/m1.jpg new file mode 100644 index 00000000..e59fd4ba Binary files /dev/null and b/docs/assets/clover-rescue-team/m1.jpg differ diff --git a/docs/assets/clover-rescue-team/m2.jpg b/docs/assets/clover-rescue-team/m2.jpg new file mode 100644 index 00000000..a01c07a5 Binary files /dev/null and b/docs/assets/clover-rescue-team/m2.jpg differ diff --git a/docs/assets/clover-rescue-team/m3.jpg b/docs/assets/clover-rescue-team/m3.jpg new file mode 100644 index 00000000..d14eed06 Binary files /dev/null and b/docs/assets/clover-rescue-team/m3.jpg differ diff --git a/docs/assets/clover-rescue-team/m4.jpg b/docs/assets/clover-rescue-team/m4.jpg new file mode 100644 index 00000000..7dd5109b Binary files /dev/null and b/docs/assets/clover-rescue-team/m4.jpg differ diff --git a/docs/assets/clover-rescue-team/m5.jpg b/docs/assets/clover-rescue-team/m5.jpg new file mode 100644 index 00000000..73ff351f Binary files /dev/null and b/docs/assets/clover-rescue-team/m5.jpg differ diff --git a/docs/assets/clover-rescue-team/main.png b/docs/assets/clover-rescue-team/main.png new file mode 100644 index 00000000..fdd036ac Binary files /dev/null and b/docs/assets/clover-rescue-team/main.png differ diff --git a/docs/assets/clover-rescue-team/mockup.png b/docs/assets/clover-rescue-team/mockup.png new file mode 100644 index 00000000..40152705 Binary files /dev/null and b/docs/assets/clover-rescue-team/mockup.png differ diff --git a/docs/assets/clover-rescue-team/s1.png b/docs/assets/clover-rescue-team/s1.png new file mode 100644 index 00000000..91cb4fac Binary files /dev/null and b/docs/assets/clover-rescue-team/s1.png differ diff --git a/docs/assets/clover-rescue-team/s2.png b/docs/assets/clover-rescue-team/s2.png new file mode 100644 index 00000000..fe3f91a7 Binary files /dev/null and b/docs/assets/clover-rescue-team/s2.png differ diff --git a/docs/assets/clover-rescue-team/s3.png b/docs/assets/clover-rescue-team/s3.png new file mode 100644 index 00000000..027dda3f Binary files /dev/null and b/docs/assets/clover-rescue-team/s3.png differ diff --git a/docs/assets/clover-rescue-team/s4.png b/docs/assets/clover-rescue-team/s4.png new file mode 100644 index 00000000..74f7b69b Binary files /dev/null and b/docs/assets/clover-rescue-team/s4.png differ diff --git a/docs/assets/clover-rescue-team/signup.png b/docs/assets/clover-rescue-team/signup.png new file mode 100644 index 00000000..9d219d3f Binary files /dev/null and b/docs/assets/clover-rescue-team/signup.png differ diff --git a/docs/assets/clover-rescue-team/status.png b/docs/assets/clover-rescue-team/status.png new file mode 100644 index 00000000..9cf278cb Binary files /dev/null and b/docs/assets/clover-rescue-team/status.png differ diff --git a/docs/assets/copter_cat/board_bottom_nums.png b/docs/assets/copter_cat/board_bottom_nums.png new file mode 100644 index 00000000..ed364bcb Binary files /dev/null and b/docs/assets/copter_cat/board_bottom_nums.png differ diff --git a/docs/assets/copter_cat/board_top_nums.png b/docs/assets/copter_cat/board_top_nums.png new file mode 100644 index 00000000..76fa7156 Binary files /dev/null and b/docs/assets/copter_cat/board_top_nums.png differ diff --git a/docs/assets/copter_cat/logo.svg b/docs/assets/copter_cat/logo.svg new file mode 100644 index 00000000..cc37fd06 --- /dev/null +++ b/docs/assets/copter_cat/logo.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/assets/djs-phoenix/1.png b/docs/assets/djs-phoenix/1.png new file mode 100644 index 00000000..c02d5a2d Binary files /dev/null and b/docs/assets/djs-phoenix/1.png differ diff --git a/docs/assets/djs-phoenix/2.png b/docs/assets/djs-phoenix/2.png new file mode 100644 index 00000000..25350a02 Binary files /dev/null and b/docs/assets/djs-phoenix/2.png differ diff --git a/docs/assets/djs-phoenix/3.png b/docs/assets/djs-phoenix/3.png new file mode 100644 index 00000000..2646b311 Binary files /dev/null and b/docs/assets/djs-phoenix/3.png differ diff --git a/docs/assets/ftl/advanced_clover_simulation.png b/docs/assets/ftl/advanced_clover_simulation.png new file mode 100644 index 00000000..ba07c18c Binary files /dev/null and b/docs/assets/ftl/advanced_clover_simulation.png differ diff --git a/docs/assets/ftl/advanced_clover_simulation_cli.png b/docs/assets/ftl/advanced_clover_simulation_cli.png new file mode 100644 index 00000000..dd549b8f Binary files /dev/null and b/docs/assets/ftl/advanced_clover_simulation_cli.png differ diff --git a/docs/assets/ftl/advanced_clover_simulation_ros.jpg b/docs/assets/ftl/advanced_clover_simulation_ros.jpg new file mode 100644 index 00000000..ed3a4e44 Binary files /dev/null and b/docs/assets/ftl/advanced_clover_simulation_ros.jpg differ diff --git a/docs/assets/lane_control_with_color.jpg b/docs/assets/lane_control_with_color.jpg new file mode 100644 index 00000000..5fbbc208 Binary files /dev/null and b/docs/assets/lane_control_with_color.jpg differ diff --git a/docs/assets/lane_control_without_any_color.jpg b/docs/assets/lane_control_without_any_color.jpg new file mode 100644 index 00000000..11093dd0 Binary files /dev/null and b/docs/assets/lane_control_without_any_color.jpg differ diff --git a/docs/assets/qgc-params.png b/docs/assets/qgc-params.png deleted file mode 100644 index dd30f324..00000000 Binary files a/docs/assets/qgc-params.png and /dev/null differ diff --git a/docs/assets/simulation_ubuntu_account.png b/docs/assets/simulation_ubuntu_account.png new file mode 100644 index 00000000..c8626369 Binary files /dev/null and b/docs/assets/simulation_ubuntu_account.png differ diff --git a/docs/assets/simulation_utm.png b/docs/assets/simulation_utm.png new file mode 100644 index 00000000..8e6c0644 Binary files /dev/null and b/docs/assets/simulation_utm.png differ diff --git a/docs/assets/stereo/q320.jpg b/docs/assets/stereo/q320.jpg new file mode 100644 index 00000000..13f65f5f Binary files /dev/null and b/docs/assets/stereo/q320.jpg differ diff --git a/docs/assets/swarm_in_blocks/fpv.jpg b/docs/assets/swarm_in_blocks/fpv.jpg new file mode 100644 index 00000000..5091c3d9 Binary files /dev/null and b/docs/assets/swarm_in_blocks/fpv.jpg differ diff --git a/docs/assets/swarm_in_blocks/swarm_preview.png b/docs/assets/swarm_in_blocks/swarm_preview.png new file mode 100644 index 00000000..013e1d4a Binary files /dev/null and b/docs/assets/swarm_in_blocks/swarm_preview.png differ diff --git a/docs/assets/swarm_in_blocks/vid.jpg b/docs/assets/swarm_in_blocks/vid.jpg new file mode 100644 index 00000000..f66530e5 Binary files /dev/null and b/docs/assets/swarm_in_blocks/vid.jpg differ diff --git a/docs/assets/zerotire/download_1.png b/docs/assets/zerotier/download_1.png similarity index 100% rename from docs/assets/zerotire/download_1.png rename to docs/assets/zerotier/download_1.png diff --git a/docs/assets/zerotire/download_2.png b/docs/assets/zerotier/download_2.png similarity index 100% rename from docs/assets/zerotire/download_2.png rename to docs/assets/zerotier/download_2.png diff --git a/docs/assets/zerotire/ios_1.png b/docs/assets/zerotier/ios_1.png similarity index 100% rename from docs/assets/zerotire/ios_1.png rename to docs/assets/zerotier/ios_1.png diff --git a/docs/assets/zerotire/ios_2.png b/docs/assets/zerotier/ios_2.png similarity index 100% rename from docs/assets/zerotire/ios_2.png rename to docs/assets/zerotier/ios_2.png diff --git a/docs/assets/zerotire/ios_3.png b/docs/assets/zerotier/ios_3.png similarity index 100% rename from docs/assets/zerotire/ios_3.png rename to docs/assets/zerotier/ios_3.png diff --git a/docs/assets/zerotire/ios_4.png b/docs/assets/zerotier/ios_4.png similarity index 100% rename from docs/assets/zerotire/ios_4.png rename to docs/assets/zerotier/ios_4.png diff --git a/docs/assets/zerotire/ios_5.png b/docs/assets/zerotier/ios_5.png similarity index 100% rename from docs/assets/zerotire/ios_5.png rename to docs/assets/zerotier/ios_5.png diff --git a/docs/assets/zerotire/linux_1.png b/docs/assets/zerotier/linux_1.png similarity index 100% rename from docs/assets/zerotire/linux_1.png rename to docs/assets/zerotier/linux_1.png diff --git a/docs/assets/zerotire/login_1.png b/docs/assets/zerotier/login_1.png similarity index 100% rename from docs/assets/zerotire/login_1.png rename to docs/assets/zerotier/login_1.png diff --git a/docs/assets/zerotire/login_2.png b/docs/assets/zerotier/login_2.png similarity index 100% rename from docs/assets/zerotire/login_2.png rename to docs/assets/zerotier/login_2.png diff --git a/docs/assets/zerotire/macos_1.png b/docs/assets/zerotier/macos_1.png similarity index 100% rename from docs/assets/zerotire/macos_1.png rename to docs/assets/zerotier/macos_1.png diff --git a/docs/assets/zerotire/macos_2.png b/docs/assets/zerotier/macos_2.png similarity index 100% rename from docs/assets/zerotire/macos_2.png rename to docs/assets/zerotier/macos_2.png diff --git a/docs/assets/zerotire/network_1.png b/docs/assets/zerotier/network_1.png similarity index 100% rename from docs/assets/zerotire/network_1.png rename to docs/assets/zerotier/network_1.png diff --git a/docs/assets/zerotire/network_2.png b/docs/assets/zerotier/network_2.png similarity index 100% rename from docs/assets/zerotire/network_2.png rename to docs/assets/zerotier/network_2.png diff --git a/docs/assets/zerotire/network_3.png b/docs/assets/zerotier/network_3.png similarity index 100% rename from docs/assets/zerotire/network_3.png rename to docs/assets/zerotier/network_3.png diff --git a/docs/assets/zerotire/network_4.png b/docs/assets/zerotier/network_4.png similarity index 100% rename from docs/assets/zerotire/network_4.png rename to docs/assets/zerotier/network_4.png diff --git a/docs/assets/zerotire/network_5.png b/docs/assets/zerotier/network_5.png similarity index 100% rename from docs/assets/zerotire/network_5.png rename to docs/assets/zerotier/network_5.png diff --git a/docs/assets/zerotire/network_6.png b/docs/assets/zerotier/network_6.png similarity index 100% rename from docs/assets/zerotire/network_6.png rename to docs/assets/zerotier/network_6.png diff --git a/docs/assets/zerotire/qgc_1.png b/docs/assets/zerotier/qgc_1.png similarity index 100% rename from docs/assets/zerotire/qgc_1.png rename to docs/assets/zerotier/qgc_1.png diff --git a/docs/assets/zerotire/qgc_2.png b/docs/assets/zerotier/qgc_2.png similarity index 100% rename from docs/assets/zerotire/qgc_2.png rename to docs/assets/zerotier/qgc_2.png diff --git a/docs/assets/zerotire/qgc_3.png b/docs/assets/zerotier/qgc_3.png similarity index 100% rename from docs/assets/zerotire/qgc_3.png rename to docs/assets/zerotier/qgc_3.png diff --git a/docs/assets/zerotire/windows_1.png b/docs/assets/zerotier/windows_1.png similarity index 100% rename from docs/assets/zerotire/windows_1.png rename to docs/assets/zerotier/windows_1.png diff --git a/docs/assets/zerotire/windows_2.png b/docs/assets/zerotier/windows_2.png similarity index 100% rename from docs/assets/zerotire/windows_2.png rename to docs/assets/zerotier/windows_2.png diff --git a/docs/assets/zerotire/windows_3.png b/docs/assets/zerotier/windows_3.png similarity index 100% rename from docs/assets/zerotire/windows_3.png rename to docs/assets/zerotier/windows_3.png diff --git a/docs/assets/zerotire/windows_4.png b/docs/assets/zerotier/windows_4.png similarity index 100% rename from docs/assets/zerotire/windows_4.png rename to docs/assets/zerotier/windows_4.png diff --git a/docs/assets/zerotire/windows_5.png b/docs/assets/zerotier/windows_5.png similarity index 100% rename from docs/assets/zerotire/windows_5.png rename to docs/assets/zerotier/windows_5.png diff --git a/docs/en/4g.md b/docs/en/4g.md index 6b549bdc..e7cb8451 100644 --- a/docs/en/4g.md +++ b/docs/en/4g.md @@ -77,7 +77,7 @@ If everything is done correctly, every time the system restarts, the service cli -> **Hint** Alternatively we recommend to use the [ZeroTier](zerotire_vpn.md) VPN-service. +> **Hint** Alternatively we recommend to use the [ZeroTier](zerotier_vpn.md) VPN-service. ## Copter control via QGroundControl diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index 7103a9cb..f45551e9 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -50,6 +50,7 @@ * [Native setup](simulation_native.md) * [VM setup](simulation_vm.md) * [Usage](simulation_usage.md) + * [Setup on M1 computers](simulation_m1.md) * [ROS](ros.md) * [MAVROS](mavros.md) * [Supplementary materials](supplementary.md) @@ -64,7 +65,7 @@ * [Model files for parts](models.md) * [ROS Melodic installation](ros-install.md) * [Camera calibration](camera_calibration.md) - * [VPN ZeroTire Connection](zerotire_vpn.md) + * [VPN ZeroTier Connection](zerotier_vpn.md) * [Quadcopter control with 4G communication](4g.md) * [Clover and Jetson Nano](jetson_nano.md) * [Remote control app](rc.md) @@ -103,6 +104,11 @@ * [Video contest](video_contest.md) * [Educational contests](educational_contests.md) * [Clover-based projects](projects.md) + * [Swarm-in-blocks](swarm_in_blocks.md) + * [Obstacle avoidance using artificial potential fields method](obstacle-avoidance-potential-fields.md) + * [The Clover Rescue Project](clover-rescue-team.md) + * [CopterCat CM4](copter_cat.md) + * [Autonomous valet parking drone assistance](djs_phoenix_ikshana.md) * [Autonomous Multirotor Landing System (AMLS)](amls.md) * [Drone show](clever-show.md) * [Innopolis Open 2020 (L22_ÆRO)](innopolis_open_L22_AERO.md) diff --git a/docs/en/autolaunch.md b/docs/en/autolaunch.md index 6fe295cb..530449d6 100644 --- a/docs/en/autolaunch.md +++ b/docs/en/autolaunch.md @@ -6,7 +6,7 @@ Software autorun systemd --- -Main documentation: [https://wiki.archlinux.org/title/Systemd](https://wiki.archlinux.org/title/Systemd). +Main documentation: https://wiki.archlinux.org/title/Systemd. All automatically started Clover software is launched as a `clover.service` systemd service. @@ -54,8 +54,8 @@ The started file must have *permission* to run: chmod +x my_program.py ``` -When scripting languages are used, a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) should be placed at the beginning of the file, for example: +When scripting languages are used, a shebang should be placed at the beginning of the file, for example: ```bash -#!/usr/bin/env python +#!/usr/bin/env python3 ``` diff --git a/docs/en/camera_calibration.md b/docs/en/camera_calibration.md index aeb997f5..5dadc9b7 100644 --- a/docs/en/camera_calibration.md +++ b/docs/en/camera_calibration.md @@ -10,14 +10,14 @@ There are several tools allowing to calibrate the camera and store calculated pa Main tutorial: http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration. -In order to calibrate the camera with the `camera_calibration` ROS-package you need a computer with OS GNU/Linux and [ROS Melodic](ros-install.md) installed. +In order to calibrate the camera with the `camera_calibration` ROS-package you need a computer with OS GNU/Linux and [ROS Noetic](http://wiki.ros.org/noetic/Installation/Ubuntu) installed. ROS Camera Calibrator 1. Using the Terminal, install `camera_calibration` package to your computer: ```bash - sudo apt-get install ros-melodic-camera-calibration + sudo apt-get install ros-noetic-camera-calibration ``` 2. Download the chessboard – [chessboard.pdf](../assets/chessboard.pdf). Print the chessboard on paper or open it on the computer screen. diff --git a/docs/en/clover-rescue-team.md b/docs/en/clover-rescue-team.md new file mode 100644 index 00000000..9ccff98f --- /dev/null +++ b/docs/en/clover-rescue-team.md @@ -0,0 +1,141 @@ +# The Clover Rescue Project + +[CopterHack-2022](copterhack2022.md), Clover Rescue Team - When something went wrong. + +## Team information + +### The list of team members + +* Кирилл Лещинский, [@k_leshchinskiy](https://t.me/k_leshchinskiy) - TeamLead. +* Кузнецов Михаил, [@fletchling_dev](https://t.me/fletchling_dev) - Software Developer. +* Даниил Валишин, [@Astel_1](https://t.me/Astel_1) - Tech Specialist/Python programmer. +* Роман Сибирцев, [@r_sibirtsev](https://t.me/r_sibirtsev) - Hardware engineer/tester. + +## Project description + +### Table of contents + +* [Idea](#idea) +* [How it works](#hiw) +* [Required hardware](#rh) +* [Operating Instructions](#oi) +* [Installation instructions](#ii) +* [Work example/functions](#functions) +* [Settings](#settings) +* [Mobile version](#mobile) +* [Bots](#bots) +* [Our plans for the future](#plans) + +## Project Idea {#idea} + +The idea of this project came immediately, it lies on the surface. A system that makes situations where pilots cannot find their flown away drone or stop it at full speed flying into a wall, a thing of the past, is something that pilots have been missing for a long time. The key feature of our software is that users can manage their Clover from anywhere in the world, this software replaces FTP and SSH (users can upload the code to their drone and run it directly from our site). Also, if radio communication with the Clover is lost, it can be returned to the user's or takeoff location with just one click. Moreover, the user can monitor the status in realtime, as well as location, camera data, and airborne position data of the drone. There are also functions that can be useful in emergency situations, such as landing, hovering and disarming the drone remotely from our website. A mobile version of the site with full functionality is also available! + +[Presentation video](https://youtu.be/4bvOu0h3YU0) + +[Functional review video](https://youtu.be/jjeBh1ch4Xo) + +[Link to the website](https://48c5-94-29-124-254.eu.ngrok.io) + + + +## How It Works {#hiw} + +Links to repositories: + +* https://github.com/DevMBS/CRTClient +* https://github.com/DevMBS/CRTClover + +The first repository is the main server that users and their drones connect to. This server provides communication and control of the drone through a user-friendly interface. +The second repository represents the server that runs on the drone and connects to the main server. This server reads and transmits telemetry to the main server (which is displayed as a 3-D visualization). It also takes commands from the user and executes them. +The Socket.IO library is used to transfer data between the client, server and drone. +After connecting the client and the drone to the server, they are added to a unique room with their UID, and already in it they exchange data. +You can learn more about how it works by visiting the repositories. + +## Required Hardware {#rh} + +All you need is COEX Clover 3/4, Raspberry Pi 3/4, USB WIFI Modem and RPi Camera! + +## Operating Instructions {#oi} + +Firstly, users need to register on our website. + + + +After registration the main control panel and installation instructions open. + +## Installation Instructions {#ii} + +First, you need to connect to your Clover via SSH and paste the command indicated in the instructions that opened (if it is not open, you can open it by clicking on the "Instructions" button). The command looks like that: + +```bash +wget https://48c5-94-29-124-254.eu.ngrok.io/assets/installers/install.sh && sudo chmod 777 ./install.sh && ./install.sh #UID +``` + +When the software is installed, the server will automatically start. After installation, you can forget about manually launching the software, it will automatically start and connect to the main server after turning on the Clover! + +## Work Example, Functions {#functions} + + + +On the website there are several commands for controlling the drone. “Get photo” allows you to get an image from the drone camera. The “Land” button lands the drone. The "Return" command returns drone to the operator, according to GPS coordinates (this requires a stable connection of the drone with 10 or more satellites). "Hover" makes the drone hover in the air. "Disarm" instantly disables the drone's motors, so you need to be careful with this command. The “Choose Code” and “Upload & Run” buttons allow users to select a code written in Python, upload it to the drone and run it. Also, users will see output of their code and all its errors. There is also interactive map with markers, blue marker the is location of the user, purple marker is the location of his drone. Also, as you can see, there is a real-time visualization of the Clover’s airborne position, as well as its altitude and the average voltage between the battery cells. + +## Settings {#settings} + + + +In the settings users can set speed and altitude of the return. + + + +Users can choose an action after return (hover or land). + + + +...And the place where the drone will return (User coordinates or takeoff coordinates). + + + +Users can also set the period for automatically receiving photos from the drone. + + + +At the top of the website is the status of your drone (Disconnected/Connected, disarmed, Connected, in flight). + + + +## Mobile Version {#mobile} + +The mobile version of the site has absolutely the same functionality (swipe to the right/left to move between control panels). + + + + + + + +## Bots on the social networks and messengers {#bots} + +Bots on the social networks is an example of what you can do based on our project. They have the main functionality of our website and The Clover Rescue Team is still working on their features. + + + + + + + + + +[VK Bot Repository](https://github.com/Astel2022/CRTVkbot) + +[Link to the bot](https://vk.com/rescueclover) + +[Demonstration video of the bot](https://youtu.be/N3oFobVCmx4) + +## Our plans for the future {#plans} + +We do not plan to stop and want to continue the development. Here is a list of what will be added. + +1. Built-in code editor. +2. More drone control bots in social networks and messengers. +3. Socket API will be written so users can create their applications in different programming languages based on our app. +4. Drone swarm controls. diff --git a/docs/en/copter_cat.md b/docs/en/copter_cat.md new file mode 100644 index 00000000..0a068583 --- /dev/null +++ b/docs/en/copter_cat.md @@ -0,0 +1,137 @@ +# CopterCat_cm4 + +[CopterHack-2022](copterhack2022.md), команда **CopterCat**. + + + +## Team Information + +Line-up: + +* Lapin Matvey (https://t.me/l_motya), engineer/programmer. +* Konovalov Evgeny (https://t.me/egnknvlv), engineer/friend. +* Skandakov Egor (https://t.me/hjbaa), friend. +* Jalilov Emil, friend. + +## Project Description + +Development of a modern board for PX4 FMUv6U firmware, 55x40 mm sizes and an additional WiFi module to implement cool things, for example, a distributed network. + +### Project idea + +Flight controller on stm32h7 with space for RPi CM4 and built-in ESP32 to create a distributed network. + +### Planned results + +FMUv6U flight controller board and API for interacting with a distributed network via RPi. + +### Using the "Clover" platform + +At the stage of the project: debugging and demonstration of capabilities. After: using CopterCat as the main one. + +## Specification + +### FMU + +* STM32H753IIK6 480Mhz Cortex-M7 +* 2Mb + 256Kb FLASH +* 1Mb RAM +* ICM20602, ICM42605, BMI088, BMP388, BMM150 +* Fully compliant with FMU-v6u standard + +### Raspberry Pi + +* Support for RPi CM4 board. +* SD card slot. +* Ability to flash the built-in eMMC. +* CAT24C256 EEPROM. +* Support 2 cameras (CAM0 is two lines, CAM1 is four lines). +* USB-OTG support. + +### ESP32 + +* 16MB external FLASH (W25Q128JVS). +* 8MB external PSRAM (LY68L6400SLIT). +* Built-in antenna. +* USB-TTL converter. + +### Remaining + +* USB-HUB USB2514B. +* USB-PD with physical switching. +* Communication between ESP32 and STM32 via UART. +* 3 power options. +* 4 universal GPIOs from ESP32. +* USB Type-C. +* Dimensions 40x55mm, board 4 layers. + +## Connectors and jumpers + +![](../assets/copter_cat/board_top_nums.png) +![](../assets/copter_cat/board_bottom_nums.png) + +1. GPIO ESP32 4 I/O ports for connecting external equipment. +2. RPi CM4 connectors. +3. ESC pins 8 pcs. +4. Programming and debugging pins for JTAG STM32. +5. Camera connectors (22 pin cable with 0.5 mm distance between conductors). +6. Contact for connecting the address tape. +7. Main power contacts 5V. +8. JST-6 standard PX4 power cable. +9. JST-6 GPS+compass+5V. +10. JST-4 I2C+5В. +11. USB Type-C. +12. JST-4 UART7+5В. +13. JST-4 I2C RPi+3.3B for connecting a rangefinder. +14. JST-4 UART5+5V. +15. JST-5 Standard connector for connecting the control receiver. +16. SD card slot (for RPi). +17. Jumper BOOT for STM32. +18. RPIBOOT jumper for flashing the RPi CM4 eMMC module. +19. Jumper for switching the USB connector operation mode (when the jumper is closed, USB works as a HUB input and when connected to a computer, STM32, ESP32 and RPi CM4 in OTG mode will be displayed; when the jumper is open, USB will work to connect external devices to the RPi, for example stereo cameras). + +## Firmware download + +### FMU + +When you first start, the microcontroller will have to load the PX4-bootloader through the JTAG port. detailed instructions [here](https://docs.px4.io/master/en/software_update/stm32_bootloader.html#stm32-bootloader). + +To connect to a computer: + +1. Close jumper 19. +2. Connect USB Type-C to your computer. +3. The device should appear in [QGC](http://qgroundcontrol.com). + +You can also flash firmware via RPi: + +1. Install the RPi CM4 into the connector on the board. +2. Open jumper 19. +3. The device will appear in the `/dev` folder on the RPi. + +### ESP32 + +You can write a program either in [Arduino IDE](https://www.arduino.cc/en/software) or in [VS Code](https://code.visualstudio.com) with the plugin [esp-idf](https://habr.com/ru/post/530638/). Then compile and upload to the microcontroller. You can download in two ways. + +From computer: + +1. Close jumper 19 +2. CP2104 will appear in the connected devices +3. Download the firmware according to the instructions for the selected IDE + +With RPi CM4: + +1. Install the RPi CM4 into the connector on the board. +2. Open jumper 19. +3. Compile your code to .bin format. +4. Upload the resulting file to the RPi. +5. Download the firmware to the microcontroller using esptool.py [description+installation](https://docs.espressif.com/projects/esptool/en/latest/esp32/index.html). + +### RPi CM4 + +The SD card slot works like a standard RPi. For boards with eMMC, the boot order of the operating system does not differ from the CM4 IO Board [instruction](https://www.jeffgeerling.com/blog/2020/how-flash-raspberry-pi-os-compute-module-4-emmc-usbboot). + +## General information + +* All files required for ordering are located in the `/gerbers` folder [here](https://github.com/matveylapin/CopterCat_cm4). +* The project was made in the program [KiCAD v6](https://www.kicad.org). +* Component Libraries are from [snapeda](https://www.snapeda.com). diff --git a/docs/en/copterhack2022.md b/docs/en/copterhack2022.md index c449a153..93f54e5a 100644 --- a/docs/en/copterhack2022.md +++ b/docs/en/copterhack2022.md @@ -8,28 +8,38 @@ You can see the articles of the CopterHack 2021 finalist teams by the link [Copt The proposed projects have to be open-source and be compatible with the Clover quadcopter platform. Teams will work on their projects throughout the competition, bringing them closer to the state of the finished product. Industry experts will assist the participants through lectures and regular feedback. +Final of the CopterHack 2022 was held on April 23, 2022. The winner team was the team 🇧🇷 **Atena - Grupo SEMEAR**. + +## Full stream of the final + + + +## Aftermovie + + + ## Projects of the contest's participants {#participants} |Place|Team|Project|Points| |:-:|-|-|-| -||🇰🇬 Alatoo University Team|[Облачная платформа для симулятора Клевера](https://github.com/pteacher/clover/blob/clover_simulator/docs/ru/clover-development-studio.md)|| -||🇧🇾 FTL|[Advanced Clover 2](https://github.com/FTL-team/clover/blob/FTL-advancedClover2/docs/ru/advanced_clover_simulator.md)|| -||🇷🇺 Stereo|[Neural obstacle avoidance](https://github.com/den250400/clover/blob/neural-obstacle-avoidance/docs/en/neural-obstacle-avoidance.md)|| -||🇷🇺 Space clowns|[Copter For Space](https://github.com/slavikyd/clover/blob/patch-3/docs/ru/c4s.md)|| -||🇷🇺 R.S.|[Drone Hawk](https://github.com/slavaroot/clover/blob/droneHawkSecurity/docs/ru/drone-hawk-security.md)|| -||🇲🇾 Moopt|[IoT Water Monitoring & Optimization](https://github.com/kafechew/clover/blob/master/docs/en/moopt-uav.md)|| -||🇧🇷 Atena - Grupo SEMEAR|[Swarm in Blocks](https://github.com/Grupo-SEMEAR-USP/clover/blob/Swarm_in_Blocks/docs/en/swarm_in_blocks.md)|| -||🇷🇺 Clevertron|[Clevertron](https://github.com/Daniel-drone/clover/blob/Clevertron-1/docs/ru/clevertron.md)|| -||🇷🇺 Clover Rescue Team|[Rescue Clover](https://github.com/DevMBS/clover/blob/CloverRescueTeam/docs/ru/clover-rescue-team.md)|| -||🇵🇱 Edgenoon|[Neural and vision-based landing method](https://github.com/edgenoon-ai/clover/blob/neural_vision_based_landing_method/docs/en/neural_vision_based_landing_method.md)|| -||🇷🇺 CopterCat|[CopterCat](https://github.com/matveylapin/clover/blob/CopterCat/docs/ru/сopter_сat.md)|| -||🇷🇺 Дрой Ронов|[Clover Swarm](https://github.com/stinger000/clever/blob/clover_swarm_request/docs/ru/clover-swarm.md)|| -||🇩🇪 Inondro|[Inondro Pix](https://github.com/Inondro/clover/blob/inondro-pix/docs/en/inondro_copterhack22_pix.md)|| -||🇮🇳 DJS Phoenix|[Autonomous valet parking drone assistance](https://github.com/DJSPhoenix/clover/blob/DJSPhoenix-Ikshana/docs/en/djs_phoenix_ikshana.md)|| -||🇷🇺 SPECTRE|[SPECTRE](https://github.com/alakhmenev/clover/blob/spectre_team/docs/ru/spectre_team.md)|| -||🇷🇺 SolidEye|[Разработка лидара без движущихся частей](https://github.com/feanorgg/clover/blob/solideye/docs/ru/solid_eye.md)|| -||🇰🇬 AI_U_CLOVER|[AIU_CLOVER](https://github.com/zhibekm/clover/blob/zhibekm-patch-1/docs/en/aiu-article.md)|| -||🇷🇺 С305|[Система мониторинга воздуха](https://github.com/Ruslan2288/clover/blob/master/docs/ru/air_monitor.md)| | +|1|🇧🇷 Atena - Grupo SEMEAR|[Swarm in Blocks](swarm_in_blocks.md)|21.6| +|2|🇧🇾 FTL|[Advanced Clover 2](../ru/advanced_clover_simulator.html)|19.9| +|3|🇷🇺 Clover Rescue Team|[Rescue Clover](clover-rescue-team.md)|17.7| +|4|🇷🇺 С305|[Система мониторинга воздуха](../ru/air_monitor.html)|17.3| +|5|🇷🇺 Space clowns|[Copter For Space](../ru/c4s.html)|16.2| +|6|🇷🇺 CopterCat|[CopterCat](copter_cat.md)|16.1| +|7|🇷🇺 Stereo|[Neural obstacle avoidance](obstacle-avoidance-potential-fields.md)|15.85| +|8|🇮🇳 DJS Phoenix|[Autonomous valet parking drone assistance](djs_phoenix_ikshana.md)|11.7| +|✕|🇷🇺 R.S.|[Drone Hawk](https://github.com/slavaroot/clover/blob/droneHawkSecurity/docs/ru/drone-hawk-security.md)|| +|✕|🇲🇾 Moopt|[IoT Water Monitoring & Optimization](https://github.com/kafechew/clover/blob/master/docs/en/moopt-uav.md)|| +|✕|🇷🇺 Дрой Ронов|[Clover Swarm](https://github.com/stinger000/clever/blob/clover_swarm_request/docs/ru/clover-swarm.md)|| +|✕|🇷🇺 SPECTRE|[SPECTRE](https://github.com/alakhmenev/clover/blob/spectre_team/docs/ru/spectre_team.md)|| +|✕|🇰🇬 Alatoo University Team|[Облачная платформа для симулятора Клевера](https://github.com/pteacher/clover/blob/clover_simulator/docs/ru/clover-development-studio.md)|| +|✕|🇷🇺 Clevertron|[Clevertron](https://github.com/Daniel-drone/clover/blob/Clevertron-1/docs/ru/clevertron.md)|| +|✕|🇵🇱 Edgenoon|[Neural and vision-based landing method](https://github.com/edgenoon-ai/clover/blob/neural_vision_based_landing_method/docs/en/neural_vision_based_landing_method.md)|| +|✕|🇩🇪 Inondro|[Inondro Pix](https://github.com/Inondro/clover/blob/inondro-pix/docs/en/inondro_copterhack22_pix.md)|| +|✕|🇷🇺 SolidEye|[Разработка лидара без движущихся частей](https://github.com/feanorgg/clover/blob/solideye/docs/ru/solid_eye.md)|| +|✕|🇰🇬 AI_U_CLOVER|[AIU_CLOVER](https://github.com/zhibekm/clover/blob/zhibekm-patch-1/docs/en/aiu-article.md)|| |✕|🇻🇳 Dragon&Tanker|[Dragon&Tanker](https://github.com/uml4/clover/blob/drone_observe_autonomous_car/docs/en/dragon_and_tanker_team.md)|| |✕|🇷🇺 V-NAV|[Visual Navigation](https://github.com/v-nav/clover/blob/v-nav_article/docs/ru/v-nav.md)|| |✕|🇷🇺 Джедаи 1581|[Ретранслятор на базе Клевера](https://github.com/JJNIK/clover/blob/patch-1/docs/ru/1581.md)|| @@ -41,6 +51,8 @@ The proposed projects have to be open-source and be compatible with the Clover q ✕ – teams which haven't qualified for the Final. +See all points by criteria in the [full table](https://docs.google.com/spreadsheets/d/1qVoXchDbaBlbFzVCyxFZDU6pp8pvC1oXasowr56tnzc). + ## Company case competition Teams are welcome to dive into the development of the following company cases: diff --git a/docs/en/djs_phoenix_ikshana.md b/docs/en/djs_phoenix_ikshana.md new file mode 100644 index 00000000..7ef1b3f6 --- /dev/null +++ b/docs/en/djs_phoenix_ikshana.md @@ -0,0 +1,69 @@ +# Ikshana — Autonomous valet parking drone assistance + +[CopterHack-2022](copterhack2022.md), team **DJS Phoenix**. + +## Team information + + + +We are the DJS Phoenix, the official drone team of Dwarkadas. J. Sanghvi College of Engineering. + +The list of team members: + +* Shubham Mehta, shubhamdmehta3257@gmail.com, Team Lead. +* Akhilesh Sadalgekar, akhilesh.sadalgekar01@gmail.com, Mechanical. +* Aman Bhatt, aman.bhatt0001@gmail.com, Mechanical. +* Manan Dedhia,manandedhia2001@gmail.com, Mechanical. +* Harshal Warde, harshal.warde@gmail.com, Mechanical. +* Karan Pandit, karandpandit26@gmail.com, Mechanical. +* Soham P Dalvi, dalvisoham710@gmail.com, Mechanical. +* Khushi Sanghvi, khushisanghvi940@gmail.com, Programming. +* Ankit Sawant, ankitsawant26@gmail.com, Electronics. +* Shubh Jatin Pokarne, shubhpokarne91@gmail.com, Electronics. +* Parth Sawjiyani, sawjiyaniparth@gmail.com, Marketing. + +## Project description + +Ikshana is a fully autonomous drone that operates in a parking lot. It scans for available parking places and then guide drivers by directing them to an optimal unoccupied parking location using LED Blinkers. + +### Project idea + +We came up with the concept of using drones to search for parking spots, this alleviates the problem of having to do it manually which is tedious and time consuming. It will help us save time and effort. + +The driver will be able to see our drone, which will lead the vehicle to the parking location. + +The drone's arms include programmable LEDs. By blinking in a relay pattern, these LEDs will direct you down the path, whether to turn right, left, continue or stop. + +The drone is equipped with sensors to maintain a safe distance in all direction and avoid obstacles. + + + +Screenshot (257) + +### The potential outcomes + +#### Problem + +Getting around parking lots can be annoying at times and finding a vacant spot in the parking lot is a time-consuming and difficult task. + +#### Solution + +Ikshana will help the driver reach an ideal parking spot. With the help of Machine Learning and ROS it will be able to find spaces for vehicle parking. + +Such a system is really helpful in parking lots as it avoids hassles and commotion while also reducing labor, being more efficient and time saving. + +By the end of the project, we expect a DIY drone that has autonomous functionality and it is eligible to perform the given task efficiently. + +### Using Clover platform + +The COEX Clover platform is used for simulating and implementing OpenCV, ROS for the drone. It helps us integrate ROS with our Raspberry Pi. + +### Additional information at the request of participants + +After two years after the COVID-19 epidemic, there is a good opportunity for the team to reunite. Previously, we spent the most of our time in our workshop. + +However, we learn to work online through various online platforms. Although procuring parts was time-consuming because very few suppliers were prepared to ship their items, and even after placing orders, delivery of those parts might take months. So, finally, we are able to collaborate and solve difficulties in a timely manner. This journey of Copterhack'22 will be full of new insights and experiences. + +## To find out more about our project visit the link below + +https://djs-phoenix.gitbook.io/ikshana/. diff --git a/docs/en/firmware.md b/docs/en/firmware.md index 38fd2407..a8c2d62f 100644 --- a/docs/en/firmware.md +++ b/docs/en/firmware.md @@ -77,19 +77,19 @@ PX4 may be compiled from the source and automatically flashed to the flight cont To do this, clone the PX4 repository: ```bash -git clone https://github.com/PX4/Firmware.git +git clone https://github.com/PX4/PX4-Autopilot.git ``` Select the appropriate version (tag) using `git checkout`. Then compile and upload the firmware: -``` -make px4fmu-v4_default upload +```bash +make px4_fmu-v4_default upload ``` -Where `px4fmu-v4_default` is the required firmware variant. +Where `px4_fmu-v4_default` is the required firmware variant. In order to upload the `v3` firmware to Pixhawk, you may need to use the `force_upload` option: -``` -make px4fmu-v3_default force-upload +```bash +make px4_fmu-v3_default force-upload ``` diff --git a/docs/en/frames.md b/docs/en/frames.md index 7eab6f12..0482fe32 100644 --- a/docs/en/frames.md +++ b/docs/en/frames.md @@ -9,7 +9,8 @@ Main frames in the `clover` package: * `base_link` is rigidly bound to the drone. It is shown by the simplified drone model on the image above; * `body` is bound to the drone, but its Z axis points up regardless of the drone's pitch and roll. It is shown by the red, blue and green lines in the illustration; * `navigate_target` is bound to the current navigation target (as set by the [navigate](simple_offboard.md#navigate) service); -* `setpoint` is current position setpoint. +* `setpoint` is current position setpoint; +* `main_camera_optical` is the coordinate system, [linked to the main camera](camera_setup.md#frame); Additional frames become available when [ArUco positioning system](aruco.md) is active: diff --git a/docs/en/laser.md b/docs/en/laser.md index 26e917ac..3bae082a 100644 --- a/docs/en/laser.md +++ b/docs/en/laser.md @@ -75,7 +75,7 @@ from sensor_msgs.msg import Range # ... -data = rospy.wait_for_message('rangefinder/range', Range) +dist = rospy.wait_for_message('rangefinder/range', Range).range ``` ### Data visualization diff --git a/docs/en/leds.md b/docs/en/leds.md index 7ee6fff6..d8606ec4 100644 --- a/docs/en/leds.md +++ b/docs/en/leds.md @@ -158,3 +158,9 @@ Current LED strip state is published in the `/led/state` ROS topic. You can view ```bash rostopic echo /led/state ``` + +Using the same topic you can get the configured number os LEDs, using Python: + +```python +led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds) +``` diff --git a/docs/en/magnetic_grip.md b/docs/en/magnetic_grip.md index b4f5a08b..23a721fb 100644 --- a/docs/en/magnetic_grip.md +++ b/docs/en/magnetic_grip.md @@ -63,3 +63,64 @@ Then connect the signal output of the circuit to the selected port and solder th 2. Use a zip tie to pull the assembled circuit to the back of the deck. 3. Plug the Arduino *D11* signal pin into one of the *AUX* pins on the flight controller. 4. Plug the power wire of the electromagnetic gripper to JST 5V. + +## Setting up electromagnetic gripper + +To control the magnet through Arduino Nano, use the following code: + +```cpp +void setup() { + pinMode(11, INPUT); + pinMode(13, OUTPUT); +} + +void loop() { + if (int duration = pulseIn(11, HIGH) > 1200) { + digitalWrite(13, HIGH); + } else { + digitalWrite(13, LOW); + } +} +``` + +To monitor the status of the electromagnetic gripper, you can connect the *ws281x* LED strip (included to Clover kit). Connect it to power +5v – 5v, ground GND – GND, and signal wire DIN – Arduino D12. + +To control the magnet and monitor it using the LED strip, use the following code: + +```cpp +#include +#define NUMPIXELS 72 +#define PIN 12 +int pin = 11; +int led = 13; + +unsigned long duration; +Adafruit_NeoPixel strip (NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); + +void setup() { + strip.begin(); + strip.setBrightness(10); + Serial.begin(9600); + pinMode(pin, INPUT); + pinMode(led, OUTPUT); +} + +void loop() { + duration = pulseIn(pin, HIGH); + Serial.println(duration); + delay(100); + if (duration >= 1500) { + digitalWrite(led, HIGH); + for (int i = -1; i < NUMPIXELS; i++) { + strip.setPixelColor(i, strip.Color(255, 0, 0)); + strip.show(); + } + } else { + digitalWrite(led, LOW); + for (int i = -1; i < NUMPIXELS; i++) { + strip.setPixelColor(i, strip.Color(0, 255, 0)); + strip.show(); + } + } +} +``` diff --git a/docs/en/obstacle-avoidance-potential-fields.md b/docs/en/obstacle-avoidance-potential-fields.md new file mode 100644 index 00000000..1a6d708c --- /dev/null +++ b/docs/en/obstacle-avoidance-potential-fields.md @@ -0,0 +1,91 @@ +# Obstacle avoidance using artificial potential fields method + +[CopterHack-2022](copterhack2022.md), team **Stereo**. + +## Team information + +The list of team members: + +* Denis Konstantinov, @den_konstantinov, engineer and developer. + +[The project repository is here](https://github.com/den250400/potential-fields-obstacle-avoidance) + +## Project description + + + +[This](https://github.com/den250400/potential-fields-obstacle-avoidance) repository contains obstacle avoidance system for quadcopters with Raspberry Pi 4 onboard computer. The code in this repository is designed to work with [Clover Raspberry Pi image](https://clover.coex.tech/en/image.html) and [special PX4-based firmware](https://clover.coex.tech/en/firmware.html) modified for easier communication with Raspberry Pi. + +Artificial potential fields method is based on considering quadcopter, obstacles and target point as electric-charged points. Quadcopter and obstacles have positive charge, and target point is assigned with negative charge. This results in quadcopter "attracting" itself to the target point, while being repelled by the same-signed charges of obstacles. Using this analogy, you can compute a safe, collision-free trajectory, which can be executed by the vehicle. + + + +It's obvious that you need some sort of geometrical information about the surrounding world if you want to avoid obstacles. This algorithm uses Intel RealSense D435 depth camera - it provides a 3D point cloud which can be easily used for potential fields computation. + +## Installation + +This guide is intended to be used on Ubuntu 20.04. Python version is 3.8.10, but it's very likely that it will work on other versions like 3.7, 3.9, 3.10 without any changes. + +1. Install the [Clover simulator](https://clover.coex.tech/en/simulation.html). +2. Install [realsense_gazebo_plugin](https://github.com/issaiass/realsense_gazebo_plugin) and [realsense2_description](https://github.com/issaiass/realsense2_description): + + ```bash + cd ~/catkin_ws/src + git clone https://github.com/issaiass/realsense_gazebo_plugin + git clone https://github.com/issaiass/realsense2_description + cd ~/catkin_ws + catkin_make + ``` + +3. Replace your `~/catkin_ws/src/clover/clover_description` folder with one in this repository. This will add RealSense D435 to quadcopter model and make other minor changes so you will be able to reproduce our results. +4. Install all necessary Python packages + + ```bash + pip3 install -r requirements.txt + sudo apt-get install ros-noetic-ros-numpy + ``` + +5. Make sure your `PYTHONPATH` env variable is set as + + ```bash + PYTHONPATH=/home//catkin_ws/devel/lib/python3/dist-packages:/opt/ros/noetic/lib/python3/dist-packages + ``` + + If it's not, add the following paths to `PYTHONPATH`. + +## Launch + +Launch the clover simulator and spawn some obstacles: + +```bash +roslaunch clover_simulation simulator.launch +``` + +Open another terminal window, and launch the takeoff script. The drone will arm its motors and take off to the altitude specified in takeoff.py script: + +```bash +python3 ./scripts/takeoff.py +``` + +Then, you should open main.py script and specify the target point (as x, y, z array). It is defined on the line + +```python +nav = AvoidanceNavigation(np.array([0, 0, 2])) +``` + +Finally, launch main.py to start the obstacle avoidance flight: + +```bash +python3 main.py +``` + +## Obstacle avoidance algorithm description + +This algorithm uses a point cloud produced by stereo camera to infer the flight path by simulating a motion of charged particle. Each point from the point cloud is considered as obstacle, and repels the vehicle with the force directed in 'point->vehicle' direction. The repelling force has a magnitude `q_repel / dist` (dist is distance from vehicle to point). Similarly, an attraction point attracts the vehicle with the constant magnitude of `q_attract`. **The sum of these force vectors is the desired flight direction.**. The speed of a particle is set as `speed` argument of `AvoidanceNavigation` and directed along the resulted force vector. This way, we get a motion equation which we can solve numerically. Now, we have the trajectory which can be executed by the vehicle. + +The algorithm is still in development, so we can't guarantee that default parameters will work in all cases. However, you may tweak the parameters yourself. Here is a brief description of each parameter in descending order of importance: + +* `speed` - if your vehicle systematically crashes, the first thing you should do is to decrease the flight speed. Values of 1.5-2 m/s show good performance on most obstacle types +* `dist_threshold` - this sets a 'sphere of influence' for individual obstacle point. Larger values of this parameter will result in trajectories with bigger margin from obstacle +* `lead` - this is a smoothing factor of trajectory execution. If this value is too big, vehicle will ignore sharp obstacle avoidance maneuvers and risks crashing into a small/thin obstacle. If this value is too small, vehicle will fly too wobbly. +* `q_repel` - the strength of repelling force is determined by this parameter. diff --git a/docs/en/optical_flow.md b/docs/en/optical_flow.md index 270d8ffe..1dd9e834 100644 --- a/docs/en/optical_flow.md +++ b/docs/en/optical_flow.md @@ -29,7 +29,7 @@ When using **EKF2** (parameter `SYS_MC_EST_GROUP` = `ekf2`): * `EKF2_OF_N_MAX` - 0.2. * `SENS_FLOW_ROT` – No rotation. * `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X) -* `SENS_FLOW_MINHGT` – 0.01 (for the rangefinder VL53L1X) +* `SENS_FLOW_MINHGT` – 0.0 (for the rangefinder VL53L1X) * Optional: `EKF2_HGT_MODE` – range sensor (cf. [rangefinder setup](laser.md)). When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, attitude_estimator_q`): @@ -41,7 +41,7 @@ When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, at * `LPE_FLW_RR` – 0.0. * `SENS_FLOW_ROT` – No rotation. * `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X) -* `SENS_FLOW_MINHGT` – 0.01 (for the rangefinder VL53L1X) +* `SENS_FLOW_MINHGT` – 0.0 (for the rangefinder VL53L1X) * Optional: `LPE_FUSION` – flag 'pub agl as lpos down' is on (see [rangefinder setup](laser.md). [The `selfcheck.py` utility](selfcheck.md) will help you verify that all settings are correctly set. diff --git a/docs/en/parameters.md b/docs/en/parameters.md index d05c7690..caef83fd 100644 --- a/docs/en/parameters.md +++ b/docs/en/parameters.md @@ -1,20 +1,64 @@ # PX4 Parameters -Main article: https://dev.px4.io/en/advanced/parameter_reference.html +Full documentation on PX4 parameters: https://docs.px4.io/master/en/advanced_config/parameter_reference.html. -> **Note** This is a description some of the most important PX4 parameters as of version 1.8.0. The full list is available at the link above. +For changing PX4 parameters, use QGroundControl software, [connect to Clover over Wi-Fi](gcs_bridge.md) or USB. Go to *Vehicle Setup* panel (click on the QGroundControl logo in the top-left corner) and choose *Parameters* menu. -To change PX4 parameters, you can use the QGroundControl application [by connecting to Clover via Wi-Fi](gcs_bridge.md): +## Recommended values -![PX4 parameters in QGroundControl](../assets/qgc-params.png) +### Common parameters -## Main parameters +|Parameter|Value|Comment| +|-|-|-| +|`SENS_FLOW_ROT`|0 (*No rotation*)|If using *PX4Flow* hardware, keep the default value| +|`SENS_FLOW_MINHGT`|0.0|For [VL53L1X](laser.md) rangefinder| +|`SENS_FLOW_MAXHGT`|4.0|For [VL53L1X](laser.md) rangefinder| +|`SENS_FLOW_MAXR`|10.0|| +|`SYS_HAS_MAG`|0|If impossible to run the magnetometer (*No mags found* error)| -The most important parameters are listed in this paragraph. +### Estimator subsystem parameters -`SYS_MC_EST_GROUP` – select the estimator module. +In case of using LPE ([COEX patched firmware](firmware.md)): -This is a group of modules that calculates the current state of the copter using readings from the sensors. The copter state includes: +|Parameter|Value|Comment| +|-|-|-| +|`LPE_FUSION`|86|Checkboxes: *flow* + *vis* + *land detector* + *gyro comp*. If flying over horizontal floor *pub agl as lpos down* checkbox is allowed.
Details: [Optical Flow](optical_flow.md), [ArUco markers](aruco_map.md), [GPS](gps.md).| +|`LPE_VIS_DELAY`|0.0|| +|`LPE_VIS_Z`|0.1|| +|`LPE_FLW_SCALE`|1.0|| +|`LPE_FLW_R`|0.2|| +|`LPE_FLW_RR`|0.0|| +|`LPE_FLW_QMIN`|10|| +|`ATT_W_EXT_HDG`|0.5|Enabling usage of external yaw angle (when navigating using [markers map](aruco_map.md))| +|`ATT_EXT_HDG_M`|1 (*Vision*)|| +|`ATT_W_MAG`|0|Disabling usage of the magnetometer (when navigating indoor)| + +In case of using EKF2 (official firmware): + + + +|Parameter|Value|Comment| +|-|-|-| +|`EKF2_AID_MASK`|26|Checkboxes: *flow* + *vision position* + *vision yaw*.
Details: [Optical Flow](optical_flow.md), [ArUco markers](aruco_map.md), [GPS](gps.md).| +|`EKF2_OF_DELAY`|0|| +|`EKF2_OF_QMIN`|10|| +|`EKF2_OF_N_MIN`|0.05|| +|`EKF2_OF_N_MAX`|0.2|| +|`EKF2_HGT_MODE`|2 (*Range sensor*)|If the [rangefinder](laser.md) is present and flying over horizontal floor| +|`EKF2_EVA_NOISE`|0.1|| +|`EKF2_EVP_NOISE`|0.1|| +|`EKF2_EV_DELAY`|0|| +|`EKF2_MAG_TYPE`|5 (*None*)|Disabling usage of the magnetometer (when navigating indoor)| + + + +> **Info** See also: list of default parameters of the [Clover simulator](simulation.md): https://github.com/CopterExpress/clover/blob/master/clover_simulation/airframes/4500_clover. + +## Additional information + +The `SYS_MC_EST_GROUP` parameter defines the estimator subsystem to use. + +Estimator subsystem is a group of modules that calculates the current state of the copter using readings from the sensors. The copter state includes: * Angle rate of the copter – pitch_rate, roll_rate, yaw_rate; * Copter orientation (in the local coordinate system) – pitch, roll, yaw (one of presentations); @@ -57,9 +101,7 @@ These parameters adjust the flight of the copter by position (POSCTL, OFFBOARD, ## LPE + Q attitude estimator -These parameters configure the behavior of the `lpe` and `q` modules, which compute the state (orientation, position) of the copter. These parameters apply **only** if the `SYS_MC_EST_GROUP` parameter is set to `1` (local_position_estimator, attitude_estimator_q) - -TODO +These parameters configure the behavior of the `lpe` and `q` modules, which compute the state (orientation, position) of the copter. These parameters apply **only** if the `SYS_MC_EST_GROUP` parameter is set to `1` (local_position_estimator, attitude_estimator_q). ## Commander @@ -68,5 +110,3 @@ Prearm checks, switching the modes and states of the copter. ## Sensors Enabling, disabling and configuring various sensors. - -TODO diff --git a/docs/en/rviz.md b/docs/en/rviz.md index a647dddc..2d2f25b7 100644 --- a/docs/en/rviz.md +++ b/docs/en/rviz.md @@ -11,7 +11,7 @@ To use rviz and rqt, a PC running Ubuntu Linux (or a virtual machine such as [Pa > **Hint** You can use the [preconfigured virtual machine image](simulation_vm.md) with ROS and Clover toolkit. -Install package `ros-melodic-desktop-full` or `ros-melodic-desktop` using the [installation documentation](http://wiki.ros.org/melodic/Installation/Ubuntu). +Install package `ros-noetic-desktop-full` or `ros-noetic-desktop` using the [installation documentation](http://wiki.ros.org/noetic/Installation/Ubuntu). Start rviz --- diff --git a/docs/en/setup.md b/docs/en/setup.md index 24b85a6f..b36a3832 100644 --- a/docs/en/setup.md +++ b/docs/en/setup.md @@ -27,28 +27,29 @@ Main article: https://docs.qgroundcontrol.com/en/SetupView/Firmware.html > **Note** Do not connect your flight controller prior to flashing. -We recommend using the modified version of PX4 by CopterExpress for the Clover drone, especially for autonomous flights. Download the latest stable version **from our GitHub**. +We recommend using the modified version of [PX4 with COEX patches](firmware.md) for the Clover drone, especially for autonomous flights. Download the latest stable version **from our GitHub**. -> **Info** For Pixhawk-based quadcopters there is a separate firmware version. See details in "[Pixhawk / Pixracer firmware flashing](firmware.md)" article. +To use all the most recent PX4 functions you also can use the latest official firmware version (experimentally). Flash the flight controller with this firmware: QGroundControl firmware upload -1. Launch QGroundControl software. -2. Open the *Vehicle Setup* tab. -3. Select the *Firmware* menu. +1. Disconnect the flight controller from computer (if connected). +2. Launch QGroundControl software. +3. Go to *Vehicle Setup* panel (click on the QGroundControl logo in the top-left corner) and select *Firmware* menu. 4. Connect your flight controller to your PC over USB. -5. Wait for the flight controller to connect to QGroundControl. -6. Select *PX4 Flight Stack* in the right bar. +5. Select *PX4 Flight Stack* in the right bar appeared. -To use the recommended Copter Express firmware: + QGroundControl firmware upload -* Check *Advanced Settings* checkbox. -* Select *Custom firmware file...* from the dropdown list. -* Press *OK* and select the file that you've downloaded. +6. To use **COEX patched firmware**: -To use the latest official stable firmware just press *OK*. + * Check *Advanced Settings* checkbox. + * Select *Custom firmware file...* from the dropdown list. + * Press *OK* and select the file that you've downloaded. + + To use the latest **official stable firmware** just press *OK*. Wait for QGroundControl to finish flashing the flight controller. @@ -82,7 +83,7 @@ This is how the main QGroundControl settings window will look like: ### Setting parameters -Open the *Vehicle Setup* tab and select the *Parameters* menu. You can use the *Search* field to find parameters by name. +Open the *Vehicle Setup* tab and select the *Parameters* menu. You can use the *Search* field to find parameters by name. Recommended parameters values are given in the further documentation and also in the [parameters summary article](parameters.md). QGroundControl parameters diff --git a/docs/en/simple_offboard.md b/docs/en/simple_offboard.md index d67ca9f4..832bd363 100644 --- a/docs/en/simple_offboard.md +++ b/docs/en/simple_offboard.md @@ -1,11 +1,4 @@ -Autonomous flight (OFFBOARD) -=== - -> **Note** In the image version **0.20** `clever` package was renamed to `clover`. See [previous version of the article](https://github.com/CopterExpress/clover/blob/v0.19/docs/en/simple_offboard.md) for older images. - - - -> **Hint** We recommend using our [custom PX4 firmware for Clover](firmware.md#modified-firmware-for-clover) for autonomous flights. +# Autonomous flight The `simple_offboard` module of the `clover` package is intended for simplified programming of the autonomous drone flight (`OFFBOARD` [flight mode](modes.md)). It allows setting the desired flight tasks, and automatically transforms [coordinates between frames](frames.md). @@ -13,8 +6,7 @@ The `simple_offboard` module of the `clover` package is intended for simplified Main services are [`get_telemetry`](#gettelemetry) (receive telemetry data), [`navigate`](#navigate) (fly to a given point along a straight line), [`navigate_global`](#navigateglobal) (fly to a point specified as latitude and longitude along a straight line), [`land`](#land) (switch to landing mode). -Python examples ---- +## Python usage You need to create proxies for services before calling them. Use the following template for your programs: @@ -37,8 +29,7 @@ land = rospy.ServiceProxy('land', Trigger) Unused proxy functions may be removed from the code. -API description ---- +## API description > **Note** Omitted numeric parameters are set to 0. @@ -312,14 +303,9 @@ Landing the drone (command line): rosservice call /land "{}" ``` - - -Additional materials ------------------------- +## Additional materials * [ArUco-based position estimation and navigation](aruco.md). * [Program samples and snippets](snippets.md). diff --git a/docs/en/simulation_m1.md b/docs/en/simulation_m1.md new file mode 100644 index 00000000..c434264b --- /dev/null +++ b/docs/en/simulation_m1.md @@ -0,0 +1,56 @@ +# Running simulator on M1 powered computer + +There is no preconfigured VM image for ARM64 architecture of M1 chip (Apple Silicon), so the only possibility is to install the simulation software manually. + +The recommended virtual machine hypervisor is [UTM app](https://mac.getutm.app/). Also it's possible to use **VMware Fusion Public Tech Preview** with M1 support. + +## Simulation installation with UTM + + + +1. Download UTM App from the official site [mac.getutm.app](https://mac.getutm.app/) and install it. +2. Download Ubuntu Linux 20.04 installation iso-file for ARM64 architecture using the link: https://cdimage.ubuntu.com/focal/daily-live/current/focal-desktop-arm64.iso. +3. Create a new virtual machine in UTM, using the following settings: + + * **Type**: Virtualize. + * **Operating System**: Linux. + * **Boot ISO Image**: choose downloaded file `focal-desktop-arm64.iso`. + * **Memory**: 4096 MB or more. + * **CPU Cores**: 4 or more. + * Turn on *Enable hardware OpenGL acceleration* option. + * **Storage**: 20 GB or more. + +4. Run the created virtual machine. +5. Choose *Install Ubuntu* in the menu and install it using the installation master. + + * Recommended apps: *Minimal installation*. + * Installation type: *Erase disk and install Ubuntu*. + * Input your account parameters, for example: + + + +6. Finish the installation and run the system. +7. Install the simulation using the [native setup manual](simulation_native.md). + +### Troubleshooting + +#### Black screen + +If you see a black screen on your virtual machine, try to run the machine without the GPU support. + +In virtual machine settings, choose *Display*, and set *Emulated Display Card* menu to *virtio-ramfb*. Run you machine. If it runs successfully, change the setting back to *virtio-ramfb-gl (GPU Supported)* and run it again. + +#### Problem with `git clone` + +The following error can occur while performing `git clone`: + +```txt +on git clone if error: RPC failed; curl 56 GnuTLS recv error (-54): Error in the pull function. +fatal: the remote end hung up unexpectedly +fatal: early EOF +fatal: index-pack failed +``` + +In this case, change the type of the network card to bridged. In the virtual machine settings, choose *Network*, and set *Network Mode* menu to *Bridged (Advanced)*. + +Later, if some network issues occur, change the network mode back to *Shared Network*. diff --git a/docs/en/simulation_native.md b/docs/en/simulation_native.md index bef84680..41d877a6 100644 --- a/docs/en/simulation_native.md +++ b/docs/en/simulation_native.md @@ -132,6 +132,12 @@ You can test autonomous flight using example scripts in `~/catkin_ws/src/clover/ ## Additional steps +To make it possible to run Gazebo simulation environment without Clover (`gazebo` command), add into your `.bashrc` sourcing Gazebo's initialization script: + +```bash +echo "source /usr/share/gazebo/setup.sh" >> ~/.bashrc +``` + Optionally, install roscore systemd service to have roscore running in background: ```bash @@ -144,10 +150,10 @@ sudo systemctl start roscore Install any web server to serve Clover's web tools (`~/.ros/www` directory), e. g. Monkey: ```bash -wget https://github.com/CopterExpress/clover_vm/raw/master/assets/packages/monkey_1.6.9-1_amd64.deb -O /tmp/monkey_1.6.9-1_amd64.deb -sudo apt-get install -y /tmp/monkey_1.6.9-1_amd64.deb +wget https://github.com/CopterExpress/clover_vm/raw/master/assets/packages/monkey_1.6.9-1_$(dpkg --print-architecture).deb -P /tmp +sudo dpkg -i /tmp/monkey_*.deb sed "s/pi/$USER/g" ~/catkin_ws/src/clover/builder/assets/monkey | sudo tee /etc/monkey/sites/default -sudo -E sh -c "sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf" +sudo sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf sudo cp ~/catkin_ws/src/clover/builder/assets/monkey.service /etc/systemd/system/monkey.service sudo systemctl enable monkey sudo systemctl start monkey diff --git a/docs/en/simulation_usage.md b/docs/en/simulation_usage.md index 8d356103..c8cc6ecb 100644 --- a/docs/en/simulation_usage.md +++ b/docs/en/simulation_usage.md @@ -40,6 +40,8 @@ In order to enable GPS sensor, set the `gps` argument in `simulator.launch` to ` ``` +Turn also on the *use GPS* flag in the `EKF2_AID_MASK` PX4 parameter (using QGroundControl). + ### Camera If you don't need the camera when flying using GPS, it may be disabled in `simulator.launch` file: diff --git a/docs/en/simulation_vm.md b/docs/en/simulation_vm.md index 10175b35..34b780ad 100644 --- a/docs/en/simulation_vm.md +++ b/docs/en/simulation_vm.md @@ -2,7 +2,7 @@ In addition to [native installation instructions](simulation_native.md), we provide a [preconfigured developer virtual machine image](https://github.com/CopterExpress/clover_vm/releases/latest). The image contains: -* Ubuntu 18.04 with XFCE lightweight desktop environment; +* Ubuntu 20.04 with XFCE lightweight desktop environment; * ROS packages required to develop for the Clover platform; * QGroundControl; * preconfigured Gazebo simulation environment; @@ -16,8 +16,6 @@ The VM is an easy way to set up a simulation environment, but can be used as a d You can download the latest VM image [in the VM releases repository](https://github.com/CopterExpress/clover_vm/releases). -> **Note** The virtual machine should be used when native installation is not feasible or possible. You may experience reduced performance in programs that use 3D rendering, like rviz and Gazebo. - ## Setting up the VM You need to use a VM manager that supports OVF format, like [VirtualBox](https://www.virtualbox.org/wiki/Downloads), [VMware Player](https://www.vmware.com/products/workstation-player.html) or [VMware Workstation](https://www.vmware.com/products/workstation-pro.html). diff --git a/docs/en/snippets.md b/docs/en/snippets.md index 8af8ca29..1d0c3a41 100644 --- a/docs/en/snippets.md +++ b/docs/en/snippets.md @@ -358,15 +358,36 @@ Enable and disable [ArUco markers recognition](aruco_marker.md) dynamically (for import rospy import dynamic_reconfigure.client -client = dynamic_reconfigure.client.Client('aruco_detect') +rospy.init_node('flight') +aruco_client = dynamic_reconfigure.client.Client('aruco_detect') # Turn markers recognition off -client.update_configuration({'enabled': False}) +aruco_client.update_configuration({'enabled': False}) rospy.sleep(5) # Turn markers recognition on -client.update_configuration({'enabled': True}) +aruco_client.update_configuration({'enabled': True}) +``` + +### # {#optical-flow-enabled} + +Enable and disable [Optical Flow](optical_flow.md) dynamically: + +```python +import rospy +import dynamic_reconfigure.client + +rospy.init_node('flight') +flow_client = dynamic_reconfigure.client.Client('optical_flow') + +# Turn Optical Flow off +flow_client.update_configuration({'enabled': False}) + +rospy.sleep(5) + +# Turn Optical Flow on +flow_client.update_configuration({'enabled': True}) ``` ### # {#wait-global-position} diff --git a/docs/en/swarm_in_blocks.md b/docs/en/swarm_in_blocks.md new file mode 100644 index 00000000..c768e481 --- /dev/null +++ b/docs/en/swarm_in_blocks.md @@ -0,0 +1,83 @@ +# Swarm-in-blocks + +## Final Video + + + +## Detailed Gitbook + +**Check our Gitbook, with the detailed information about all that was developed by Atena Team during CopterHack 2022: https://swarm-in-blocks.gitbook.io/swarm-in-blocks/introduction/swarm-in-blocks**. + +>The Clover Platform was forked and adapted to work with swarms. The Swarm in Blocks project is a separated repository with all our swarm manipulation tools for clover. Link of the fork of the Clover Platform: https://github.com/Grupo-SEMEAR-USP/clover . Link of the Swarm in Blocks repository: https://github.com/Grupo-SEMEAR-USP/swarm_in_blocks . + +## Introduction + +Nowadays, swarms of drones are getting more and more applications and being used in several different areas, from agriculture to surveillance, and rescues, but controlling a high amount of drones usually isn't a simple task, demanding a lot of studies and complex software. Swarm in Blocks was born looking to make a high-level interface based on the blocks language, to make simple handling swarms, without requiring advanced knowledge in all the necessary platforms, creating tools to allow a lot of applications based on the user needs and also using the Clover platform, which has a lot of advantages as being complete and intuitive, supporting all the project goals. + + + +## Usability + +### How it works + +The Swarm in Blocks can be programmed either with the blocks interface or directly in Python and we developed three main launch modes, each one focused on a different application of the project, they are: + +- **Planning Mode:** Its main goal is to allow the user to check the drones' layout, save and load formations, before starting the simulator or using real clovers. In order to need less computational power and avoid possible errors during the simulation. +- **Simulation Mode:** In this mode happens the simulation indeed, starting the Gazebo, the necessary ROS nodes and some other tools. It allows applying the developed features, which will be explained ahead and see how they would behave in real life. +- **Navigation Mode:** The last mode will support executing everything developed in real clovers so that it's possible to control a swarm with block programming. The biggest obstacle yet is the practical testing of this mode, due to the financial difficulty to afford a Clover swarm. + +### Blocks Interface + +The entire Swarm in Blocks project was designed so that the user was in an intuitive and comfortable environment within the manipulation of swarms, for this, the existing platform with clover packages was completely rethought and adapted. In our [gitbook](https://app.gitbook.com/s/C9O11TiXK1JPnlrpilLg/usability/blocks-api), we have more details about the front-end design, how the user can interact with it, and achieve our main goal: programming in blocks. + + + +### Features + +Along with the project, we developed some features, that can be used together or independently and also serve as base for the implementation of more specific and advanced innovations. Here are the list and a brief explanation of each tool, to see more details and instructions about their use, check our [gitbook](https://app.gitbook.com/s/C9O11TiXK1JPnlrpilLg/background-theory/system)! + +- **Formations:** There are some types of formation that were developed in order to create figures and other images, for uses in engineering and spectacles. They are: + + - 2D Formations: We made functions to generate some simple geometries, allowing the user to set the number of clovers used and the size of the figure, the geometries options made until now are *circle*, *empty square*, *full square* and *triangle*. + - 3D Formations: Besides the 2D figures, there are also some simple 3D geometries, which are *cube*, *sphere*, and *pyramid*. + - Alphabet: There is also the option to generate letters and words, using our Clover swarm. + - 3D Figures: Lastly, we have the alternative to make other more complex 3D formations, for this we use a library called Open3D that deals with 3D data, allowing the drones to create any 3D image the user inputs since it's in the supported formats. + +- **Transformations:** In addition to creating the formations, it's important to give the option of editing their disposition, so some operations were developed. It also makes possible to execute more complex actions and activities with the Clovers. The transformations operations are: + + - Scale: Changes the distance between the drones, increasing or decreasing the image. + - Translate: All the drones move the same distance in the chosen directions. + - Rotate: The formation rotates around a determined axis. + + + +- **LED effects:** Enjoying the LEDs included in the Clover, we made some functions to apply effects in all the swarm, creating figures and operations with the LEDs too. + + + +- **Swarm Preview:** The main goal of this feature is to help the user to visualize how the swarm will behave in the simulation or real life, without using a lot of computational power and avoiding some problems in the simulation. This way a 2D or 3D image illustrating the drones' disposition can pop up on the screen when using this function. + + + +- **First Person View (FPV):** The FPV node makes it a lot easier to visualize each drone's camera individually and also control each one of them at a time using keyboard bindings. + + + +All these features can be very useful for some applications and also be attractive to arouse the curiosity of the general public. + +## Conclusion + +Over the last months we studied a lot, grew, and surpassed our limits, trying to explore some swarm applications, all to deliver the best possible project: **Swarm in Blocks**. Our motivation was to facilitate such a complex task as the manipulation of swarms of drones, through block programming, and it delighted us a lot and we hope it will fascinate all our users. We tried to resume all the project and its features in this article, but as there are many details and needed explanations, it was made a [gitbook](https://swarm-in-blocks.gitbook.io/swarm-in-blocks/introduction/swarm-in-blocks), to explore them for those who are interested. + +For us, the results achieved were very expressive and positive, however, we believe that there is still room for improvement in the project, both considering the robustness of the swarm and the simplification of the usability of our platform. Improvement in the collision avoidance system, implementation of more formations and tests in real Clovers are some of the points that we hope to develop in future opportunities. + +Finally, we thank the entire COEX team that made it possible for CopterHack 2022 to take place and all the support given during the competition. We are **Atena Team**, creator of the **Swarm in Blocks** platform and we thank you for your attention! + +### The Atena Team members + +- Guilherme Soares Silvestre : [GitHub](https://github.com/guisoares9), [LinkedIn](https://www.linkedin.com/in/guilherme-soares-silvestre-76570118b/) +- Eduardo Morelli Fares: [GitHub](https://github.com/faresedu), [LinkedIn](https://www.linkedin.com/in/eduardo-fares-a271561a0/) +- Felipe Tommaselli: [GitHub](https://github.com/Felipe-Tommaselli), [LinkedIn](https://www.linkedin.com/in/felipe-tommaselli-385a9b1a4/) +- João Aires C. F. Marsicano: [GitHub](https://github.com/Playergeek181), [LinkedIn](https://www.linkedin.com/in/joao-aires-correa-fernandes-marciano-53b426195/) +- José Carlos Andrade do Nascimento: [GitHub](https://github.com/joseCarlosAndrade), [LinkedIn](https://www.linkedin.com/in/jos%C3%A9-carlos-andrade-do-nascimento-71186421a) +- Rafael Saud C. Ferro: [GitHub](https://github.com/Rafael-Saud), [LinkedIn](https://www.linkedin.com/in/rafael-saud/) diff --git a/docs/en/web_video_server.md b/docs/en/web_video_server.md index b84b25d2..dd797168 100644 --- a/docs/en/web_video_server.md +++ b/docs/en/web_video_server.md @@ -20,7 +20,7 @@ Parameters `width`, `height`, etc. re also available. Read more about `web_video ## Browse with rqt_image_view -To browse images with the rqt tools the user needs a computer with Ubuntu 18.04 and [ROS Melodic](http://wiki.ros.org/melodic/Installation/Ubuntu). +To browse images with the rqt tools the user needs a computer with Ubuntu 20.04 and [ROS Noetic](http://wiki.ros.org/noetic/Installation/Ubuntu). [Connect to the Clover Wi-Fi network](wifi.md) an run `rqt_image_view` with its IP-address: diff --git a/docs/en/zerotire_vpn.md b/docs/en/zerotier_vpn.md similarity index 68% rename from docs/en/zerotire_vpn.md rename to docs/en/zerotier_vpn.md index a99b85b8..ed88c46f 100644 --- a/docs/en/zerotire_vpn.md +++ b/docs/en/zerotier_vpn.md @@ -4,35 +4,35 @@ 1. Go to [ZeroTier](https://www.zerotier.com/) website. - + 2. Sign up on ZeroTier. - + 3. Go to your account. 4. Click on the *Create A Network*. - + 5. After that, you will see the network you created, its ID and name. Click on the network to configure it. - + 6. In the window that appears you can change the network name and connection privacy. - + 7. Scroll down to the *Members* column. It will say that there are no users on the network. - + 8. Devices connected to the network will be displayed in this column. To allow them to connect to the network, activate the *Auth?* checkbox. The connected device will automatically be given an internal IP address, which will then be used to communicate with this device.
- - + +
> **Hint** specify names for new devices, it will help you distinguish them from each other in the future. @@ -47,17 +47,17 @@ 1. Go to the ZeroTier website. - + 2. Click on the Windows icon. - + 3. Download and run the `ZeroTier One.msi` file.
- - + +
### Network connection @@ -68,11 +68,11 @@ 3. Click on the *Join Network...* to connect to the network. - + 4. In the window that appears, enter your network ID and click *Join*. - + 5. Allow using the new network. @@ -82,11 +82,11 @@ 1. Go to the ZeroTier website. - + 2. Click on the iOS icon. - + 3. Install the *ZeroTier One* app. @@ -96,23 +96,23 @@ 2. Click on *+* to add a new connection. - + 3. Confirm the privacy policy. - + 4. Enter your network ID and click *Add Network*. - + 5. Confirm adding the new VPN configuration. 6. Connect to the VPN network by sliding the network activation slider.
- - + +
## Setup on Linux (PC, Raspberry Pi) @@ -133,7 +133,7 @@ 2. Enter the command `sudo zerotier-cli join network-id`, where `network-id` is your network ID. - + 3. If the connection is successful, the corresponding message will be displayed in the console. @@ -143,11 +143,11 @@ 1. Go to the ZeroTier website. - + 2. Click on the macOS icon. - + 3. Download and run `ZeroTier One.pkg` file. @@ -161,23 +161,23 @@ 3. In the window that appears, click on *Join Network...*. - + 4. In the *Enter Network ID* field, enter your network ID. - + ## Connecting to the copter 1. Make sure that ZeroTier is working and connected to the network on the drone and control device. To do this, make sure that these have an *Online* status. - + 2. Make sure that all devices have local IP addresses - *Managed IPs*. 3. Open GQC and in the *Comm Links* tab add a TCP connection specifying the IP of the drone. Read more about remote connection [here](gcs_bridge.md).
- - + +
diff --git a/docs/ru/4g.md b/docs/ru/4g.md index c565cf65..fdda9440 100644 --- a/docs/ru/4g.md +++ b/docs/ru/4g.md @@ -77,7 +77,7 @@ sudo systemctl enable openvpn-client@config-name -> **Hint** В качестве удобной альтернативы вы можете воспользоваться VPN-сервисом [ZeroTier](zerotire_vpn.md). +> **Hint** В качестве удобной альтернативы вы можете воспользоваться VPN-сервисом [ZeroTier](zerotier_vpn.md). ## Управление коптером через QGroundControl diff --git a/docs/ru/SUMMARY.md b/docs/ru/SUMMARY.md index 7b7d419d..127d16da 100644 --- a/docs/ru/SUMMARY.md +++ b/docs/ru/SUMMARY.md @@ -51,6 +51,7 @@ * [Сборка на собственной машине](simulation_native.md) * [Установка виртуальной машины](simulation_vm.md) * [Использование симулятора](simulation_usage.md) + * [Установка на компьютеры c M1](simulation_m1.md) * [ROS](ros.md) * [MAVROS](mavros.md) * [Дополнительные материалы](supplementary.md) @@ -68,7 +69,7 @@ * [Docker-контейнер с симулятором](sitl_docker.md) * [Установка ROS Melodic](ros-install.md) * [Калибровка камеры](camera_calibration.md) - * [Подключение к VPN ZeroTire](zerotire_vpn.md) + * [Подключение к VPN ZeroTier](zerotier_vpn.md) * [Подключение к VPN Hamachi](hamachi_vpn.md) * [Управление мультикоптером при помощи 4G связи](4g.md) * [Пакеты Клевера на Jetson Nano](jetson_nano.md) @@ -101,6 +102,7 @@ * [Светодиодная лента (legacy)](leds_old.md) * [Вклад в Клевер](contributing.md) * [Репозиторий пакетов COEX](packages.md) + * [Тестирование Клевера](testing.md) * [Переход на версию 0.20](migrate20.md) * [Переход на версию 0.22](migrate22.md) * [COEX DuoCam](duocam.md) @@ -117,6 +119,11 @@ * [Конкурс видео](video_contest.md) * [Образовательные конкурсы](educational_contests.md) * [Проекты на базе Клевера](projects.md) + * [Advanced Clover Simulator](advanced_clover_simulator.md) + * [Copter For Space](c4s.md) + * [CopterCat CM4](copter_cat.md) + * [Система мониторинга воздуха](air_monitor.md) + * [Контроль соблюдения ПДД на выделенной полосе с дроном](lane_control.md) * [Система автоматической посадки (AMLS)](amls.md) * [Разработка системы для управления БПЛА с помощью шлема виртуальной реальности](remote-control-with-oculusvr.md) * [Шоу коптеров](clever-show.md) diff --git a/docs/ru/advanced_clover_simulator.md b/docs/ru/advanced_clover_simulator.md new file mode 100644 index 00000000..dbb88139 --- /dev/null +++ b/docs/ru/advanced_clover_simulator.md @@ -0,0 +1,83 @@ +# Advanced Clover Simulator + +![Cloversim image](../assets/ftl/advanced_clover_simulation.png) + +[CopterHack-2022](copterhack2022.md), команда **FTL**. + +[Ссылка на проект](https://github.com/FTL-team/clover_sim). + +## Информация о команде + +```cpp +#include "interestingCommandDescription.h" +``` + +Состав команды: + +- Максим Романовский, [@maximmasterr](https://t.me/maximmasterr), капитан, программист. +- Михаил Кулик, [@mkulik05](https://t.me/mkulik05), программист. + +Страна: Беларусь. + +## Описание проекта + +Разработка симулятора для Clover с возможностью создания workspace (окружения, позволяющие легко управлять и транспортировать файлы симулятора такие как: проекты, конфигурации, установленные пакеты). + +- Простой, быстрый и производительный способ запуска симулятора. +- Инструментарий для создания заданий (как на [IOR2020](https://clover.coex.tech/ru/innopolis_open_L22_AERO.html)). +- Изоляция Gazebo и чекера заданий от Workspace пользователя и clover. +- Worker для проверки задач со степика. +- Возможность запуска несколько коптеров каждый из которых имеет свой контейнер. +- Полная симуляция raspberry с помощью qemu (включая GPIO, USB и камеру). + +### Использование платформы "Клевер" + +Данный проект является набором инструментов для запуска симуляторов Клевера. + +## Первые пуски + +Для начала мы решили разработать симулятор имеющий такой же функционал как и [виртуальная машина](./simulation_vm.md) + +В качестве инструменты для запуска контейнеров мы используем `systemd-nspawn` ([read more](https://wiki.archlinux.org/title/systemd-nspawn)), так как он запускает systemd init и позволяет пользоваться `systemctl`, так как: не эмулируется ядро линукс, отсутствует ограничение на процессор и память, производительность такого метода выше чем у виртуальной машины. + +С графикой посложнее, в виртуальной машине используется графика от VMware, однако в случае если мы не эмулируем ядро мы могли бы пробросить видеокарту. В отличии от виртуальной машины видеокарта останется доступна в хост машине, однако данный метод требует что бы внутри были установлены такие же драйвера как и в хосте(привет Nvidia). Поэтому мы используем `virgl` ([read more](https://gitlab.freedesktop.org/virgl/virglrenderer)), а точнее [virgl_test_server](https://gitlab.freedesktop.org/virgl/virglrenderer/-/wikis/vtest). VirGL представляет собой очень производительный способ проброса OpenGL (и Vulkan). Из-за бага в mesa требуется чтобы стояла mesa 21, для этого мы добавляем ppa [kisak-mesa](https://launchpad.net/~kisak/+archive/ubuntu/kisak-mesa). Работает это примерно так: virgl_test_server создаёт unix-сокет, он пробрасывается в контейнер, mesa в контейнере отправляет запросы на рендер в этот сокет, VirGL принимает их и транслирует в видеокарту, результат отображается на экране. Такой способ позволяет получить более 120fps в Gazebo. + +Для реализации механизма workspace, мы используем [overlayfs](https://wiki.archlinux.org/title/Overlay_filesystem), она позволяет создать фс в которой файлы модифицируются и читаются в "верхней" файловой системе, а за тем по порядку в нижних. Таким образом у нас есть директория base которая хранит внутри симулятор, ubuntu, clover. А workspace являются папки поверх base которые хранят только изменения что позволяет сильно экономить место. Workspace можно быстро создавать и удалять, экспортировать и импортировать. + +Для сборки base мы используем GitHub Actions, [здесь](https://GitHub.com/FTL-team/clover_sim_basefs) находится репозиторий со скриптами, из интересного можно отметить то что требуется прописать в `/etc/resolv.conf` dns-серверы, прописать нормальный hostname в `/etc/hostname` (иначе debootstrap скопирует его с runner-а GitHub, и оно будет странным) и прописать в `/etc/hosts` строку для резолва этого имени на `127.0.0.1` иначе ROS не запустится. + +Для того чтобы было всё красиво и удобно мы создали [инструмент](https://github.com/FTL-team/clover_sim) на Go, который занимается управлением workspace-ами и запуском симулятора. + +При попытке запуска симулятора происходило полное поглощение оперативной памяти и swap, а это 8+32 гигабайта. В чём же дело? А в том, что rosout для того чтобы проверить не упрётся ли оно в лимит файловых дескрипторов... аллоцирует их все :/ Гениальное решение. Решается добавление лимитов в `/etc/security/limits.d/30-nofilelimit.conf`. [Более подробно здесь](https://answers.ros.org/question/336963/rosout-high-memory-usage/). + +## Сеть, раздельные контейнеры, внутренности systemd и ROS + +Для того чтобы обеспечить лучшую переносимость workspace-ов и большую безопасность логично разделить симулятор на две части: Gazebo и clover. Часть Gazebo запускает внутри себя Gazebo и проверяющий скрипт, а часть clover запускает эмулятор PX4, сервисы clover и пользовательские программы. Для упрощения разработки и обеспечения большой безопасности в каждом контейнере должен запускаться свой roscore. + +И чтобы это всё могло общаться между собой необходимо чтобы симулятор создавал свою виртуальную сеть. Имеется [очень хорошая статья](https://medium.com/techlog/diving-into-linux-networking-and-docker-bridge-veth-and-iptables-a05eb27b1e72) на эту тему, она рассказывает про тот как создаётся виртуальная сеть в докере. В отличие от докера systemd имеет возможность автоматической настройки network namespace если передать ему bridge что достаточно упрощает нашу задачу. Итак что происходит при запуске симулятора мы создаём bridge под названием `cloversim` и задаём ему IP `192.168.77.1`, такой IP будет у хост системы, [команды которые это делают](https://github.com/FTL-team/clover_sim/blob/15d194f7855be0436c9f1a0145dc331971bfeffd/src/network.go#L22-L24). Так же для того чтобы всё правильно работало необходимо выделять и присваивать IP адреса контейнерам. Выделение реализованно достаточно просто: если при запуске контейнера был указан рекомендуемый IP то начинаем поиск свободного с него, иначе c IP `192.168.77.10`, поиск происходит простой инкрементацией последнего числа, [реализация](https://github.com/FTL-team/clover_sim/blob/fe2ce8e343e06d668c155b5304dc54dc23d10445/src/network.go#L87-L99). Для того чтобы настроить сеть в контейнере внутри него выполняется команда `ip addr add /24 dev host0 && ip link set host0 up && ip route add default via 192.168.77.1`, она присваивает IP, поднимает подключение и говорит что все пакеты следует слать через него. Маршрутизацией пакетов между контейнером и хостом занимается bridge, а вот для того чтобы контейнеры могли получить доступ к интернету нужно поднять небольшой [NAT](https://ru.wikipedia.org/wiki/NAT). В статье упомянутой раньше используется iptables, но вместо него теперь рекомендуется nftables. Работает это примерно так: создать таблицу роутинга, добавить цепочку правил к таблице, добавить правило которое применяет nat. [Реализация](https://github.com/FTL-team/clover_sim/blob/fe2ce8e343e06d668c155b5304dc54dc23d10445/src/network.go#L42-L44). + +О запуске внутри systemd-nspawn. Можно подумать что запуск внутри контейнера происходит как то так: `systemd-nspawn exec ls`, однако это в корни не так. Запускать команды нужно как-то так `systemd-run --machine=clover0 -t /bin/bash -c "ls"`. Что же такое systemd-run? Это инструмент systemd который создаёт временный systemd-сервис и запускает его. Кстати для того чтобы контролировать systemd-сервисы внутри контейнера нужно использовать `systemctl --machine=clover0 start SSHd.service`. И так для любых команд для работы с systemd. Как бы это не казалось странным всё дело в упрощении, для того чтобы не внедрять дополнительный функционал по запуску команд в systemd-nspawn, проще устанавливать соединение с systemd внутри контейнера и просить его создать и запустить сервис. У такого подхода есть свои плюсы: мы можем запускать команды после того как определённые сервисы запустились, мы получаем мощь сервисных файлов systemd для запуска команд, не нужно заново реализовывать логирование. Что касается создания соединения с systemd в контейнере, на самом деле systemctl (и др.) умеют подключаться не только к контейнерам но и к удалённым SSH машинам, так же это необходимо для того чтобы systemctl (и др.) могли подключаться не к системному systemd а к юзерскому (такая возможность также существует). Так что как бы не казалось монолитной структура systemd изначальна, в действительности она очень красиво реализована. + +![Как происходит подписка на топик в ROS](../assets/ftl/advanced_clover_simulation_ros.jpg) + +Далее для того чтобы контейнеру с clover было доступна камера и LED-лента, которой управляет симулятор. За работу камеры отвечают топики `/main_camera/image_raw` и `/main_camera/camera_info`, для работы LED нужен топик `/led/state` и сервис `/led/set_leds`. Конечно можно просто написать сервер который будет получать ROS-сообщения и отправлять их клиенту который будет публиковать их в другом клиенте. Но такой подход не самый производительный, по этому мы реализовали скрипт на Python который получает информацию из одного ROS-мастера и публикует его в другой, и в результате ноды разных мастеров общаются между собой напрямую. ROS мастер выполняет функцию связывающего который сообщает на каких адресах находятся какие ноды и какие топики они публикуют/подписаны, так же он сообщает адрес по которому доступна нода для сервисов, [более подробно](http://wiki.ROS.org/ROS/Technical%20Overview). Соответственно, для того чтобы сервисы и топики стали видны необходимо передать информацию из одного мастера в другой. Для этого можно обращаться напрямую к [ROS Master API](http://wiki.ROS.org/ROS/Master_API), API ROS представляет собой набор функций вызываемых с помощью xmlrpc. Для того чтобы "пробросить топик" из мастера А в мастер Б, мы подписываемся на нужный топик, получаем информацию о том кто его публикует, отправляем эту информацию в мастер B, топик становится доступным в мастере B. С сервисами ситуация чуть проще, при вызове сервиса клиент каждый раз запрашивает мастер про адрес по которому нужно вызывать сервис, так как сервис может представлять только одна нода то мы просто с определённой периодичностью запрашиваем информацию о сервисе у мастера A и отправляем её в мастер B. Вот так это всё реализовано: [тык](https://github.com/FTL-team/clover_sim_basefs/blob/main/files/cloversim/scripts/sim_proxy). + +Launch-файл не сильно поменялся по сравнению с [прошлой версией](https://github.com/CopterExpress/clover/blob/master/clover_simulation/launch/simulator.launch). Во первых он был разнесён на две части: первая часть это - `Gazebo instance` и `Clover model`. Во вторую часть попали `PX4 instance` и `Clover services`. Так же в первую часть был добавлен запуск ROS-прокси. Во второй части была задана [переменная окружения](https://github.com/FTL-team/clover_sim_basefs/blob/0d2b12f9ff07916dd525d00206a552f1aa6e3cb3/files/cloversim/launch/copter.launch#L19) которая говорила PX4 что симулятор находиться в первом контейнере и gcs_bridge был переключён на UDP чтобы QGroundControl мог автоматически подключаться к контейнерам. Итоговые файлы: [контейнер с симулятор](https://github.com/FTL-team/clover_sim_basefs/blob/main/files/cloversim/launch/simulator.launch), [контейнер с clover-ом](https://github.com/FTL-team/clover_sim_basefs/blob/0d2b12f9ff07916dd525d00206a552f1aa6e3cb3/files/cloversim/launch/copter.launch). + +[Видео](https://youtu.be/8k-gAUIeyis) с демонстрацией работы после описанного выше. + +## Задачи, Catkin и Gazebo + +Нам было необходимо реализовать какой-то метод для управления симулятором. Первой идеей было добавление подкоманд в clover_sim, тогда перезапуск симулятора выглядел бы примерно так: `sudo ./clover_sim simulator restart`. У такого подхода есть две проблемы: во-первых каждый раз надо набирать `sudo ./clover_sim`, во вторых нужно как-то(например с помощью unix-сокетов) реализовать обмен данными между запущенной командой и `sudo ./clover_sim launch` который контролирует всё. Поэтому мы решили реализовать консоль внутри `sudo ./clover_sim launch` которая позволит управлять симулятором. [Эта статья](https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html) очень помогла сделать консоль удобной. [Реализация внутри clover_sim](https://github.com/FTL-team/clover_sim/blob/main/src/simulatorControl.go). Выглядит это так: + +![Консоль](../assets/ftl/advanced_clover_simulation_cli.png) + +Далее для упрощения распространения мы решили перенести ROS-пакет с нашим симулятором из base_fs в отдельный слой overlay, мы собираем этот слой с помощью systemd-nspawn без boot флага(работает почти так же как простой chroot). Для того чтобы не собирать слой с нашим пакетом каждый раз мы сверяем package.xml в исходниках нашего пакета с package.xml в слое и если они не совпадают мы пересобираем слой. Также не стоит забывать про то что при сборке нужно подгружать переменные окружения. + +### Задания + +Задания представляют собой ROS-пакеты которые способны создавать миры Gazebo и проверять пользовательские решения, сами задания загружаются из папки tasks при launch-е. Так же как и в слое с cloversim мы храним задание в отдельном overlay слое, и при запуске cloversim сверяет package.xml в задании с package.xml в слое и если они не совпадают мы пересобираем слой. Следующая проблема это как нам вызвать что нибудь в задаче (например генерацию мира) тут на помощь приходит "особенность" catkin которая позволяет установить питоновский пакет в название независимое от название ROS-пакета. И используя эту особенность каждое задание вне зависимости от названия доступен так: `from cloversim_task.world import WORLD`. Файл который ложит пакет под одним и тем же именем: [тык](https://github.com/FTL-team/clover_sim/blob/main/sim/cloversim/setup.py). Кроме того нам необходимо запускать генерацию мира из roslaunch перед тем как запускать ноды типа Gazebo или clover. На помощь приходит фича roslaunch которая позволяет положить в ROS-параметр результат команды, [документация про это](http://wiki.ros.org/roslaunch/XML/param). Вот так это выглядит в roslaunch: [тык](https://github.com/FTL-team/clover_sim/blob/fb34a6ff81a8e67f2d5744b555a0f20df0aa61d1/sim/cloversim/launch/simulator.launch#L11-L12). Также для того чтобы не стартовать симулятор раньше времени у нас есть механизм синхронизации между контейнерами: roslaunch в контейнере с clover запускает TCP сервер и ждёт коннекта и выходит затем, в свою очередь контейнер с симулятором после генерации мира подключается к контейнеру с clover вследствие чего команды roslaunch в обоих контейнерах продолжает выполнение и запускает ноды. + +Переходим к тому как задание работает с миром. Во-первых генерация, миры Gazebo описаны с помощью [SDFormat](http://sdformat.org/spec?elem=world) основанном на XML, и для генерации миров мы просто написали несколько классов (куб, цилиндр, модель из файла) которые генерирует мир в SDFormat и сохраняют его. Для того чтобы можно было использовать в качестве текстуры не только текст но и изображения мы реализуем [функцию](https://github.com/FTL-team/clover_sim/blob/main/sim/cloversim/src/cloversim/generation/image.py) которая генерирует ogre-скрипты которые подаются в Gazebo и рендерят картинку. Так же нам необходимо иметь возможность создавать модели с разными текстурами на разных гранях, для того чтобы это сделать например [для куба](https://github.com/FTL-team/clover_sim/blob/fb34a6ff81a8e67f2d5744b555a0f20df0aa61d1/sim/cloversim/src/cloversim/generation/basic.py#L150-L179) нам нужно создавать 6 тонких кубов и размещать их там где должны находится грани. Другой способ сделать разные текстуры на разных поверхностях это использование COLLADA-файлов, но мы отказались от этой идеи так как кроме генератора SDFormat нам пришлось бы делать генератор COLLADA что сильно усложнило бы библиотеку. Далее для того чтобы миры не были каждый раз одинаковыми необходимо добавить возможность рандомизации, на помощь приходит псевдо-рандом который позволяет генерировать псевдо-случайные значения используя seed, один seed один и тот же результат рандомизации. Использование seed так же позволяет легко переносить различные рандомизации и тестировать их по порядку. Теперь переходим к тому как проверять задания Gazebo предоставляет [ROS-сервисы](http://gazebosim.org/tutorials/?tut=ros_comm#Services:Stateandpropertygetters) которые позволяют получить текущее положение вещей. Так что для определения позиции коптера нам нужно просто вызвать get_model_state, а для проверки на arm коптера можно проверять скорость в `get_joint_state`. + +## [Видео с демонстрацией](https://www.youtube.com/watch?v=mO9yoeeEdsw) diff --git a/docs/ru/air_monitor.md b/docs/ru/air_monitor.md new file mode 100644 index 00000000..c579adc4 --- /dev/null +++ b/docs/ru/air_monitor.md @@ -0,0 +1,51 @@ +# Система мониторинга воздуха + +[CopterHack-2022](copterhack2022.md), команда **С305**. + + + +## Информация о команде + +Состав команды: + +* [Мамбетов Руслан](https://github.com/Ruslan2288) - CEO, разработчик встраиваемого ПО, дизайнер печатной платы; +* [Филимонов Сергей](https://github.com/Lukerrr) - программист (интерфейс пользователя, визуализатор); +* [Смадыч Никита](https://github.com/NikitaS2001) - инженер-программист (навигация дрона); +* [Антонов Георгий](https://github.com/GeJudas23) - инженер (сборка и настройка дрона, создание 3D-моделей). + +## Описание проекта + +### Идея проекта + +Идея представляемого проекта заключается во внедрении в платформу “Клевера” дополнительных модулей предназначенных для измерения содержания в воздухе газов и органических соединений используемых в промышленности, а также лидара (RPLIDAR), предназначенного для автономной навигации. + +Наши нововведения позволят использовать коптер для поиска участков с повышенным содержанием потенциально опасных для человека и окружающей среды газообразных веществ, строить карту загрязненности воздуха и обозначать на ней зоны с критической концентрацией газов. Также дрон сможет выполнять задачи по обнаружению утечек на газовых магистралях. В совокупности это даст возможность обнаруживать локальные зоны выброса опасных газов и впоследствии своевременно их устранять. + +Применение лидара обусловлено необходимостью облета дроном возможных препятствий при выполнении миссии, либо работы в замкнутых помещениях, например, локализация зон утечек вредных газов в больших закрытых производственных и складских помещениях. + +Проект, в том числе, направлен на мониторинг углеродного следа, являющимся важным фактором в вопросе глобального изменения климата. Статистика показывает, что повышение уровня выбросов парниковых газов в атмосферу за последние годы стремительно растет, что приводит к повышению температуры поверхности планеты и значительному увеличению числа природных катастроф и аномалий. Примером влияния глобального потепления являются и недавние события - аномальная жара летом 2021 года. + +### Презентационный ролик + +[![](https://img.youtube.com/vi/zkYE8mLS81k/0.jpg)](https://www.youtube.com/watch?v=zkYE8mLS81k) + +## [Репозиторий проекта](https://github.com/Lukerrr/air-analysis-system) + +### Документация к проекту + +* [Сборка коптера газоанализатора](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/drone_assembly.md) +* [Настройка коптера для автономных полётов](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/drone_config.md) +* [Система планирования полета](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/path_planning.md) +* [Пользовательские приложения](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/user_applications.md) +* [Настройка лидара](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/setup_lidar.md) +* [Процесс загрузки прошивки](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/firmware_upload.md) +* [Подключение датчиков газов](https://github.com/Lukerrr/air-analysis-system/blob/master/docs/gas_sensors.md) + +### Ресурсы проекта + +* [Исходный код программ для Raspberry Pi](https://github.com/Lukerrr/airmon) +* [Исходный код наземной станции](https://github.com/Lukerrr/airmon-control) +* [Модели крепления лидара](https://github.com/Lukerrr/air-analysis-system/tree/master/models) +* [Прошивка для газоанализатора (Arduino)](https://github.com/Lukerrr/air-analysis-system/tree/master/hardware/arduino_firmware) +* [Прошивка для газоанализатора (STM)](https://github.com/Lukerrr/air-analysis-system/tree/master/hardware/stm_firmware) +* [Gerber файлы платы газоанализатора](https://github.com/Lukerrr/air-analysis-system/tree/master/hardware/gerber_pcb_air_analysis_board) diff --git a/docs/ru/autolaunch.md b/docs/ru/autolaunch.md index f840f8a1..c3f44682 100644 --- a/docs/ru/autolaunch.md +++ b/docs/ru/autolaunch.md @@ -6,7 +6,7 @@ systemd --- -Основная документация: [https://wiki.archlinux.org/index.php/Systemd_(Русский)](https://wiki.archlinux.org/index.php/Systemd_(Русский)). +Основная документация: https://wiki.archlinux.org/index.php/Systemd_(Русский). Все автоматически стартуемое ПО Клевера запускается в виде systemd-сервиса `clover.service`. @@ -22,7 +22,7 @@ sudo systemctl restart clover journalctl -u clover ``` -Для того, запустить ПО Клевера непосредственно в текущей консольной сессии, вы можете использовать `roslaunch`: +Для того чтобы запустить ПО Клевера непосредственно в текущей консольной сессии, вы можете использовать `roslaunch`: ```bash sudo systemctl stop clover @@ -54,8 +54,8 @@ roslaunch chmod +x my_program.py ``` -При использовании скриптовых языков вначале файла должен стоять [shebang](https://ru.wikipedia.org/wiki/Шебанг_(Unix)), например: +При использовании скриптовых языков вначале файла должен стоять shebang, например: ```bash -#!/usr/bin/env python +#!/usr/bin/env python3 ``` diff --git a/docs/ru/c4s.md b/docs/ru/c4s.md new file mode 100644 index 00000000..12b2b20b --- /dev/null +++ b/docs/ru/c4s.md @@ -0,0 +1,182 @@ +# «Copter For Space» (C4S) – принимаем данные со спутника на Клевер + + + +[CopterHack-2022](copterhack2022.md), **команда Space clowns**. + +## Информация о команде + +Состав команды: + +* Илья Холодилов, https://t.me/ilyazxz, TeamLead, программист-инженер. +* Вячеслав Демьяненко, https://t.me/SlavikYD, инженер-программист. +* Андрей Пивоваров, https://t.me/DedAndrew, программист. +* Ярослав Мухатдинов, https://t.me/Euenot2, инженер-конструктор. +* Тимур Малышкин, https://t.me/Timur_Malyshkin, аналитик космических снимков и геопространственных данных (декодирование и постобработка данных), TechWriter. + +## Видео о проекте + +[![](https://i.ytimg.com/vi/g4s14v2qtwU/0.jpg)](https://youtu.be/g4s14v2qtwU) + +## Описание проекта + +Создание инженерного конструктора станции приёма данных L-диапазона с метеорологических спутников на базе «Clover-4.2». + +Наша цель - сделать конструктор доступнее, чтобы вовлечь как можно больше школьников в прикладные исследования в областях летающей робототехники, программирования, а также метеорологии. + +### Идея проекта + +#### Аперитив + +Однажды пятеро джентльменов, любуясь туманными горами Кабардино-Балкарии, решили проверить дерзкую, не имеющую аналогов в мире идею. А что если вместо программируемой руки-манипулятора или тяжёлой тросово-опорной конструкции, для переноса облучателя над фокальной плоскостью комплекса приёма космической информации использовать дрон или же сеть дронов? + +Прежде чем начать повествование об инновационной разработке, Вы, дорогой читатель, должны понимать основной принцип работы станций приёма космических данных. Предлагаем Вам ознакомиться с обучающими вебинарами (https://youtu.be/adclrJgpJWg), где на примере комплекса «Link To Space», разработанного инженерной компанией «LoReTT», описываются основные аспекты приёма, декодирования, обработки, постобработки, а также анализа космических снимков. + +#### Шоу начинается + +В далёком 1963-м году, в 15-ти километрах от маленького пуэрто-риканского городка Аресибо, была открыта одноимённая радиообсерватория. В 1974 году именно оттуда было отправлено легендарное «Послание Аресибо», адресованное внеземным цивилизациям. Помимо этого, данная радиообсерватория принимала активное участие в изучении дальнего космоса. В 2020-ом году, из-за усталости конструкции, лопнул один из тросов, удерживающих облучатель, что повлекло за собой разрушение не только облучателя, но и, частично, «зеркала» приёмного комплекса. На восстановление комплекса уйдут годы и миллионы долларов, поэтому Корнеллский университет и Научный фонд США – главные операторы обсерватории, - сочли нецелесообразным восстановление объекта. Кто знает, может быть, США могли бы совершить ещё не одно открытие в области изучения космоса. + + + + + +Но давайте немного отыграем ситуацию назад. А что если вместо программируемой руки-манипулятора или тяжёлой тросово-опорной конструкции, для переноса облучателя над фокальной плоскостью комплекса приёма космической информации использовать дрон или же сеть дронов? А если это возможно, то почему бы не создать инженерный конструктор для школьных технологических кружков? + +### Использование платформы Клевер + +В качестве устройства для переноса облучателя над фокальной плоскостью был выбран «Clover 4 Code». Выбор Клевера – не случаен. Данный дрон имеет широкий спектр выполняемых задач, возможности для модификации и самостоятельной доработки под конкретные цели. Поскольку предполагается перенос массивного модуля электроники с облучателем, мы поставили на Клевер более мощные моторы и аккумулятор увеличенной ёмкости, установили улучшенные винты. Скорректировав PID-ы, Клевер без проблем взлетал и выполнял полётное задание с подвешенным на него оборудованием. + +#### От идеи до практической реализации + +Первым делом наши инженеры создали модели блока электроники, облучателя, опор коптера. Нельзя не отметить факт перераспределения ударной нагрузки на ножки в 3-х направлениях, что повышает срок службы оборудования, а также «выживаемость» дрона в случае аварийной посадки. + + + + + +Блок электроники включает в себя всю необходимую аппаратуру для приёма и записи сигнала со спутника: SDR-приёмник, малошумящий усилитель (МШУ), блок питания МШУ, микрокомпьютер «Raspberry-Pi 4», облучатель. + + + + + +Следующим шагом стало написание программной части. Было создано ПО для автономного полёта и записи принимаемого сигнала. Запись сигнала осуществлялась на Raspberry. + + + +https://github.com/petayyyy/Lorett/ (ссылка на наш репозиторий). + +Итак, в чём же новизна нашей разработки? Вместо роботизированного манипулятора, облучатель над «тарелкой» переносит дрон. Клевер взлетает и начинает полёт по заранее рассчитанной траектории приёма спутниковых данных (траектория рассчитывается на основе орбитальных параметров конкретного космического аппарата). Координаты пролёта спутника конвертируются в координаты маркеров, которые закреплены над плоскостью тарелки на радиопрозрачном баннере. Ориентируясь по ArUco-маркерам, Клевер пролетает по заданной траектории, принимая сигнал со спутника на облучатель и записывая его через SDR в память микрокомпьютера. По завершению пролёта, дрон совершает посадку. Для визуализации принципа действия дрона, предлагаем Вам взглянуть на фрагмент симуляции: + +https://youtu.be/tWtlljBtSvw + +После посадки дрона, мы переносим принятый сигнал на рабочий ноутбук, где приступаем к демодулированию и декодированию. Затем, можно начинать постобработку и анализ полученных данных. С помощью тематического ПО и анализа спутниковых снимков в разных цветовых каналах мы можем «вытащить» из них следующие данные: температура воды и подстилающей поверхности/верхней границы облаков, тип подстилающей поверхности, альбедо поверхности, тип облаков, водозапас облаков. Полученные данные помогут составить точный прогноз погоды для территории, с которой был принят сигнал. + +Примеры обработанных снимков: + + + + + + + + + +Пример снимка без обработки: + + + +#### Взлёт разрешён + +Положив рядом жгут, много перевязочного материала, пустырник, мы преступили к испытаниям. Полёты проходили в разных местах нашей Родины, а также в разные сезоны. Первая серия полётов была в Нальчике (Кабардино-Балкарская Республика) на образовательной программе ОЦ «Сириус» в марте 2021 года. Горный ветер не щадил никого, ни уши испытателей, ни Клевер. Было много сомнений насчёт практической применяемости нашей новинки, но в конечном счёте, мы приняли сигнал. Результат декодирования произвёл эффект разорвавшейся бомбы. На экране ноутбука появилась полоска, шириной в несколько пикселей, которую смог воспринять HRPT-Reader (ПО для постобработки). Данное изображение было возможно просмотреть в разных цветовых каналах. Это могло значить только одно – при более «плавном» перемещении дрона, мы имеем все шансы получить идеальное изображение, как со станции «Link2Space». + +Принятая полоска: + + + +Видео тестовых полетов: https://youtu.be/xP1Ne3j95zU + +Вторые полётные испытания, после частичной доработки дрона (установка более мощных моторов, новых пропеллеров, доработка ПО), прошли в подмосковной Кубинке, на конкурсе «ИнтЭРА», проходившего в рамках международного военно-технического форума «Армия-2021». Казалось бы, всё должно пройти гладко, но одновременно с нами, на полигоне Алабино (в радиусе 5 км от нас) проходили «Международные армейские игры», где соревновались, в том числе военные-связисты. И результат их работы мы целиком и полностью ощутили на себе. Их средства подавления сигналов различного рода работали безотказно, что даже при приёме мощнейшего метеоспутника (китайский FengYun) мы получали 9 Гб «белого шума». + +Белый шум: + + + +Видео с испытаний: https://youtu.be/k1ORpj3o-ew + +#### Доработка проекта в рамках "CopterHack 2022" + +### Модификация конструкции дрона + +Были доработаны опоры "Клевера" таким образом, что блок электроники и облучатель стали лучше защищены в случае аварийной посадки дрона. Также был решен вопрос совместимости компонентов и подобрана их наилучшая конфигурация. Каждый компонент так и просится на свое место. + +https://petayyyy.gitbook.io/copter-for-space/more/dev/3d-modeli-detalei - Ссылка на 3D-модели деталей + +https://disk.yandex.ru/d/SrQ9xaMjKvO6vw - STL-файлы моделей + +### Специальное оборудование + +Приёмная часть состоит из облучателя L-диапазона (ряд металлизированных дисков разных размеров, закрепленных при помощи шпильки на некотором расстоянии друг от друга), малошумящего усилителя соответствующих частот, тройника смешения(специальное устройство, позволяющее подавать питание на МШУ и принимать сигнал по одному проводу) и Программируемого радиоприемника (airspy sdr). + +МШУ (Малошумящий усилитель) – устройство, входящее в состав базовой станции (БС) и используемое для повышения чувствительности приемника в восходящем направлении UL (Uplink). + +Software-defined radio/SDR (рус.Программно определяемая радиосистема) — радиопередатчик и/или радиоприёмник, использующий технологию, позволяющую с помощью программного обеспечения устанавливать или изменять рабочие радиочастотные параметры, включая, в частности, диапазон частот, тип модуляции или выходную мощность, за исключением изменения рабочих параметров, используемых в ходе обычной предварительно определённой работы с предварительными установками радиоустройства, согласно той или иной спецификации или системы. + +Блок питания МШУ - регуляция входного и выходного напряжений. + +Облучатель − сосредоточенный элемент параболической антенны, находящийся в её фокусе (фазовом центре) или фокальной плоскости, формирующий диаграмму направленности и поляризацию антенны. + +### Программное обеспечение + +Основываясь на открытом программном коде был создан автоматический демодулятор-декодер (далее Д-Д), который мы назвали "SatDump". Позднее, возможности Д-Д были оптимизированы для Raspberry Pi с некоторыми дополнительными возможностями. Например - автоматическая демодуляция и декодирования данных с метеорологических спутников с возможностью просмотра спутниковых снимков в браузере. + +https://github.com/petayyyy/Lorett// - исходный код + +https://gitlab.com/lpmrfentazis/HRPTAutoDecoder - автодекодер. + +https://gitlab.com/lpmrfentazis/lorettorbital/-/blob/develop/lorettOrbital/orbital.py - библиотека “LoReTT Orbital” для расчёта расписания пролётов спутников и создания траектории перемещения облучателя дроном в фокальной плоскости “зеркала” + +https://github.com/petayyyy/Lorett// - сборник ПО для функционирования комплекса-конструктора, описанного в "ГитБуке" + +### Документация + +Концепция конструктора подразумевает из себя возможность освоения обучающимися определенных компетенций в областях инжиниринга, программирования, радиотехники, летающей робототехники и работы с данными дистанционного зондирования Земли. Для комфортного освоения мы подготовили все необходимые учебные материалы: + +https://disk.yandex.ru/i/hJjB1w0ekQ0Lug - руководство по эксплуатация комплекса. + +https://petayyyy.gitbook.io/copter-for-space/ - инструкция по сборке, настройке и эксплуатации комплекса в удобном формате “ГитБука”. + +https://disk.yandex.ru/d/WOpfbO74N2cmPQ - инструкция по демодуляции и декодированию сигнала с метеорологических спутников, а также анализу полученных космических снимков. + +### Результаты + +1. Подготовлен сборник подробной документации по работе с комплексом-конструктором. +2. Доработан конструктив дрона, улучшена “живучесть” и сбалансированность компонентов. +3. Проведены образовательные мероприятия на площадках в Гимназии МГУ и “ФизТех-Лицее им.Капицы”, в ходе которых обучающиеся произвели монтаж и настройку комплекса-конструктора, а также выполнили сеансы приема данных с метеоспутников. После образовательной программы в ФТЛ был создан “ГитБук” для удобства работы с учебными материалами. +4. Создано новое ПО для расчета расписания пролетов спутников и, в соответствии с параметрами ИСЗ, выбора оптимальной траектории движения дрона-манипулятора в фокальной плоскости “зеркала”. Разработан автоматический демодулятор-декодер “SatDump”. + +#### Список дополнительных компонентов + +Основные доп.компоненты: + +* DIATONE MAMBA TOKA 2207.5/2450KV - моторы. +* HQProp DP6X4X3 PC (2 пары) - пропеллеры. +* ONBO 4200mAh 4S 35C Lipo Pack - аккумулятор. + +Для более подробного ознакомления со списком компонентов и узнать, где приобрести данные комплектующие вы можете перейти в таблицу по ссылке ниже: + +https://docs.google.com/spreadsheets/d/19Gwm3lu31WgYOmuQ6WVm2aCMhhrhTMLTLWVspxvzfWs/edit#gid=0 + +Для приобретения специального оборудования, свяжитесь с ИК "LoReTT", ввиду эксклюзивности данных компонентов. + +Контакты: ООО "Лоретт", Россия, г. Москва, Инновационный центр "Сколково", Большой бульвар, 42, стр. 1, офис 334, 121205. + ++7 (985) 727-7630. + +contact@lorett.org. + +#### Перспективы + +Хочется отметить перспективы использования дрона или сети дронов для переноса облучателя на крупных приёмных комплексах. В будущем возможно использовать платформу Pelican. Данная конструкция значительно упростит обслуживание крупных антенн, а также снизит количество потенциальных узлов отказа. + +Безусловно, работа школьников с конструктором развивает в них не только инженерно-технические компетенции, но и научно-исследовательские! Платформа “Clover” предоставляет широкий спектр возможностей для модификации, кто знает, может быть, найдётся Левша, который модифицирует исходный конструктор так, что нам и не снилось! Познание мира - в экспериментах. А команда “SpaceClowns”, совместно с компанией “LoReTT” поможет в формировании будущих “Кулибиных”. diff --git a/docs/ru/calibration.md b/docs/ru/calibration.md index 42ee9cbd..30c299eb 100644 --- a/docs/ru/calibration.md +++ b/docs/ru/calibration.md @@ -14,6 +14,8 @@ 4. Последовательно устанавливайте квадрокоптер в каждую из указанных ориентаций до появления желтой рамки. 5. Вращайте квадрокоптер по направлению стрелки до появления зеленой рамки. +> **Warning** Последние версии прошивки PX4 не поддерживают внутренний компас на полетном контроллере COEX Pix. При появлении ошибки *No mags found* перейдите во вкладку *Parameters*, установите параметры `SYS_HAS_MAG` в `0`, `EKF2_MAG_TYPE` в `None` и перезагрузите полетный контроллер (*Tools* => *Reboot Vehicle*). + Дополнительная информация: https://docs.px4.io/master/en/config/compass.html. ## Гироскоп diff --git a/docs/ru/camera_calibration.md b/docs/ru/camera_calibration.md index 798d8215..e29da39b 100644 --- a/docs/ru/camera_calibration.md +++ b/docs/ru/camera_calibration.md @@ -10,14 +10,14 @@ Основной туториал: http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration. -Для калибровки камеры с использованием ROS-пакета camera_calibration необходим компьютер с установленным ОС GNU/Linux и [ROS Melodic](ros-install.md). +Для калибровки камеры с использованием ROS-пакета camera_calibration необходим компьютер с установленным ОС GNU/Linux и [ROS Noetic](http://wiki.ros.org/noetic/Installation/Ubuntu). ROS Camera Calibrator 1. Используя Терминал, установите на компьютер пакет `camera_calibration`: ```bash - sudo apt-get install ros-melodic-camera-calibration + sudo apt-get install ros-noetic-camera-calibration ``` 2. Скачайте калибровочную доску – [`chessboard.pdf`](../assets/chessboard.pdf). Распечатайте доску на принтере либо выведите ее на экран компьютера. diff --git a/docs/ru/copter_cat.md b/docs/ru/copter_cat.md new file mode 100644 index 00000000..61e61552 --- /dev/null +++ b/docs/ru/copter_cat.md @@ -0,0 +1,137 @@ +# CopterCat_cm4 + +[CopterHack-2022](copterhack2022.md), команда **CopterCat**. + + + +## Информация о команде + +Состав команды: + +* Лапин Матвей (https://t.me/l_motya), инженер/программист. +* Коновалов Евгений (https://t.me/egnknvlv), инженер/друг. +* Скандаков Егор (https://t.me/hjbaa), друг. +* Джалилов Эмиль, друг. + +## Описание проекта + +Разработка современной платы под прошивку PX4 FMUv6U, разерами 55x40 мм и дополнительным модулем WiFi для реализации классных штук, например, распределённой сети. + +### Идея проекта + +Полётник на stm32h7 с местом для RPi CM4 и встроенной ESP32 для создания распределённой сети. + +### Планируемые результаты + +Плата полётного контроллера FMUv6U и API для взаимодействия с распределённой сетью через RPi. + +### Использование платформы "Клевер" + +На этапе проекта: отладка и демонстрация возможностей. После: использования CopterCat в качестве основного. + +## Спецификация + +### FMU + +* STM32H753IIK6 480Mhz Cortex-M7 +* 2Mb + 256Kb FLASH +* 1Mb RAM +* ICM20602, ICM42605, BMI088, BMP388, BMM150 +* Полностью совместима со стандартом FMU-v6u + +### Raspberry Pi + +* Поддержка платы RPi CM4. +* Слот для SD-карты. +* Возможность прошивки встроенной eMMC. +* CAT24C256 EEPROM. +* Поддержка 2-х камер (CAM0-две линия, CAM1-четыре линии). +* Поддержка USB-OTG. + +### ESP32 + +* 16MB внешней FLASH (W25Q128JVS). +* 8MB внешней PSRAM (LY68L6400SLIT). +* Встроенная антенна. +* USB-TTL конвертор. + +### Остальное + +* USB-HUB USB2514B. +* USB-PD с физическим переключением. +* Связь ESP32 и STM32 через UART. +* 3 варианта питания. +* 4 универсальных GPIO от ESP32. +* USB Type-C. +* Размеры 40x55 мм, плата 4 слоя. + +## Разъёмы и перемычки + +![](../assets/copter_cat/board_top_nums.png) +![](../assets/copter_cat/board_bottom_nums.png) + +1. GPIO ESP32 4 порта ввода вывода для подключения внешнего оборудования. +2. Коннекторы RPi CM4. +3. Выводы ESC 8 шт. +4. Контакты программирования и отладки JTAG STM32. +5. Коннекторы камер (шлейф 22 контакта с 0.5 мм расстояния между проводниками). +6. Контакт подключения адресной ленты. +7. Контакты основного питания 5В. +8. JST-6 стандартного шлейфа питания PX4. +9. JST-6 GPS+компас+5В. +10. JST-4 I2C+5В. +11. USB Type-C. +12. JST-4 UART7+5В. +13. JST-4 I2C RPi+3.3B для подключения дальномера. +14. JST-4 UART5+5В. +15. JST-5 Стандартный разъём для подключения приёмника управления. +16. Слот SD карты (для RPi). +17. Джампер BOOT для STM32. +18. Джампер RPIBOOT для прошивки eMMC модуля RPi CM4. +19. Джампер переключения режима работы USB разъёма (при замкнутой перемычке, USB работает как вход HUB и при подключении к компьютеру будут отображаться STM32, ESP32 и RPi CM4 в режиме OTG; при разомкнутой перемычке, USB будет работать для подключения внешних устройств к RPi, например стереокамеры). + +## Загрузка прошивки + +### FMU + +При первом запуске, в микроконтроллер придётся загрузить PX4-bootloader через порт JTAG. Подробная инструкция [здесь](https://docs.px4.io/master/en/software_update/stm32_bootloader.html#stm32-bootloader). + +Для подключения к компьютеру: + +1. Замкните перемычку 19. +2. Подключите USB Type-C к компьютеру. +3. Устройство должно появиться в [QGC](http://qgroundcontrol.com). + +Также прошивку можно произвести через RPi: + +1. Установите RPi CM4 в коннектор на плате. +2. Разомкните перемычку 19. +3. Устройство появиться в папке `/dev` на RPi. + +### ESP32 + +Написать программу можно либо в [Arduino IDE](https://www.arduino.cc/en/software), либо в [VS Code](https://code.visualstudio.com) с плагином [esp-idf](https://habr.com/ru/post/530638/). Далее скомпилировать и загрузить в микроконтроллер. Загрузить можно двумя способами. + +С компьютера: + +1. Замкните перемычку 19 +2. В подключённых устройствах появиться CP2104 +3. Загрузите прошивку в соответствии с инструкцией к выбранной IDE + +С RPi CM4: + +1. Установите RPi CM4 в коннектор на плате. +2. Разомкните перемычку 19. +3. Скомпилируйте ваш код в .bin формат. +4. Загрузите полученный файл на RPi. +5. Загрузите прошивку в микроконтроллер с помощью esptool.py ([описание+установка](https://docs.espressif.com/projects/esptool/en/latest/esp32/index.html)). + +### RPi CM4 + +Слот SD карты работает как на стандартной RPi. Для плат с eMMC порядок загрузки операционной системы не отличается от CM4 IO Board ([инструкция](https://www.jeffgeerling.com/blog/2020/how-flash-raspberry-pi-os-compute-module-4-emmc-usbboot)). + +## Общая информация + +* Все требуемые для заказа файлы находятся в папке `/gerbers`. +* Проект выполнен в программе [KiCAD v6](https://www.kicad.org). +* Библиотеки компонентов взяты с сайта [snapeda](https://www.snapeda.com). diff --git a/docs/ru/copterhack2022.md b/docs/ru/copterhack2022.md index 1765ecba..a81a1aa1 100644 --- a/docs/ru/copterhack2022.md +++ b/docs/ru/copterhack2022.md @@ -8,28 +8,38 @@ CopterHack 2022 — это международный конкурс по ра На конкурс принимаются проекты с открытым исходным кодом и совместимые с платформой квадрокоптера "Клевер". На протяжении конкурса команды работают на собственными идеями и разработками, приближая их к состоянию готового продукта. В этом участникам помогают эксперты отрасли через лекции и регулярную обратную связь. +Финал конкурса CopterHack 2022 прошел 23 апреля 2022. Победителями стала команда 🇧🇷 **Atena - Grupo SEMEAR**. + +## Полный стрим финала + + + +## Видео + + + ## Проекты участников конкурса {#participants} |Место|Команда|Проект|Балл| |:-:|-|-|-| -||🇰🇬 Alatoo University Team|[Облачная платформа для симулятора Клевера](https://github.com/pteacher/clover/blob/clover_simulator/docs/ru/clover-development-studio.md)|| -||🇧🇾 FTL|[Advanced Clover 2](https://github.com/FTL-team/clover/blob/FTL-advancedClover2/docs/ru/advanced_clover_simulator.md)|| -||🇷🇺 Stereo|[Neural obstacle avoidance](https://github.com/den250400/clover/blob/neural-obstacle-avoidance/docs/en/neural-obstacle-avoidance.md)|| -||🇷🇺 Space clowns|[Copter For Space](https://github.com/slavikyd/clover/blob/patch-3/docs/ru/c4s.md)|| -||🇷🇺 R.S.|[Drone Hawk](https://github.com/slavaroot/clover/blob/droneHawkSecurity/docs/ru/drone-hawk-security.md)|| -||🇲🇾 Moopt|[IoT Water Monitoring & Optimization](https://github.com/kafechew/clover/blob/master/docs/en/moopt-uav.md)|| -||🇧🇷 Atena - Grupo SEMEAR|[Swarm in Blocks](https://github.com/Grupo-SEMEAR-USP/clover/blob/Swarm_in_Blocks/docs/en/swarm_in_blocks.md)|| -||🇷🇺 Clevertron|[Clevertron](https://github.com/Daniel-drone/clover/blob/Clevertron-1/docs/ru/clevertron.md)|| -||🇷🇺 Clover Rescue Team|[Rescue Clover](https://github.com/DevMBS/clover/blob/CloverRescueTeam/docs/ru/clover-rescue-team.md)|| -||🇵🇱 Edgenoon|[Neural and vision-based landing method](https://github.com/edgenoon-ai/clover/blob/neural_vision_based_landing_method/docs/en/neural_vision_based_landing_method.md)|| -||🇷🇺 CopterCat|[CopterCat](https://github.com/matveylapin/clover/blob/CopterCat/docs/ru/сopter_сat.md)|| -||🇷🇺 Дрой Ронов|[Clover Swarm](https://github.com/stinger000/clever/blob/clover_swarm_request/docs/ru/clover-swarm.md)|| -||🇩🇪 Inondro|[Inondro Pix](https://github.com/Inondro/clover/blob/inondro-pix/docs/en/inondro_copterhack22_pix.md)|| -||🇮🇳 DJS Phoenix|[Autonomous valet parking drone assistance](https://github.com/DJSPhoenix/clover/blob/DJSPhoenix-Ikshana/docs/en/djs_phoenix_ikshana.md)|| -||🇷🇺 SPECTRE|[SPECTRE](https://github.com/alakhmenev/clover/blob/spectre_team/docs/ru/spectre_team.md)|| -||🇷🇺 SolidEye|[Разработка лидара без движущихся частей](https://github.com/feanorgg/clover/blob/solideye/docs/ru/solid_eye.md)|| -||🇰🇬 AI_U_CLOVER|[AIU_CLOVER](https://github.com/zhibekm/clover/blob/zhibekm-patch-1/docs/en/aiu-article.md)|| -||🇷🇺 С305|[Система мониторинга воздуха](https://github.com/Ruslan2288/clover/blob/master/docs/ru/air_monitor.md)| | +|1|🇧🇷 Atena - Grupo SEMEAR|[Swarm in Blocks](../en/swarm_in_blocks.html)|21.6| +|2|🇧🇾 FTL|[Advanced Clover 2](advanced_clover_simulator.md)|19.9| +|3|🇷🇺 Clover Rescue Team|[Rescue Clover](../en/clover-rescue-team.html)|17.7| +|4|🇷🇺 С305|[Система мониторинга воздуха](air_monitor.md)|17.3| +|5|🇷🇺 Space clowns|[Copter For Space](c4s.md)|16.2| +|6|🇷🇺 CopterCat|[CopterCat](copter_cat.md)|16.1| +|7|🇷🇺 Stereo|[Neural obstacle avoidance](../en/obstacle-avoidance-potential-fields.html)|15.85| +|8|🇮🇳 DJS Phoenix|[Autonomous valet parking drone assistance](../en/djs_phoenix_ikshana.html)|11.7| +|✕|🇷🇺 R.S.|[Drone Hawk](https://github.com/slavaroot/clover/blob/droneHawkSecurity/docs/ru/drone-hawk-security.md)|| +|✕|🇲🇾 Moopt|[IoT Water Monitoring & Optimization](https://github.com/kafechew/clover/blob/master/docs/en/moopt-uav.md)|| +|✕|🇷🇺 Дрой Ронов|[Clover Swarm](https://github.com/stinger000/clever/blob/clover_swarm_request/docs/ru/clover-swarm.md)|| +|✕|🇷🇺 SPECTRE|[SPECTRE](https://github.com/alakhmenev/clover/blob/spectre_team/docs/ru/spectre_team.md)|| +|✕|🇰🇬 Alatoo University Team|[Облачная платформа для симулятора Клевера](https://github.com/pteacher/clover/blob/clover_simulator/docs/ru/clover-development-studio.md)|| +|✕|🇷🇺 Clevertron|[Clevertron](https://github.com/Daniel-drone/clover/blob/Clevertron-1/docs/ru/clevertron.md)|| +|✕|🇵🇱 Edgenoon|[Neural and vision-based landing method](https://github.com/edgenoon-ai/clover/blob/neural_vision_based_landing_method/docs/en/neural_vision_based_landing_method.md)|| +|✕|🇩🇪 Inondro|[Inondro Pix](https://github.com/Inondro/clover/blob/inondro-pix/docs/en/inondro_copterhack22_pix.md)|| +|✕|🇷🇺 SolidEye|[Разработка лидара без движущихся частей](https://github.com/feanorgg/clover/blob/solideye/docs/ru/solid_eye.md)|| +|✕|🇰🇬 AI_U_CLOVER|[AIU_CLOVER](https://github.com/zhibekm/clover/blob/zhibekm-patch-1/docs/en/aiu-article.md)|| |✕|🇻🇳 Dragon&Tanker|[Dragon&Tanker](https://github.com/uml4/clover/blob/drone_observe_autonomous_car/docs/en/dragon_and_tanker_team.md)|| |✕|🇷🇺 V-NAV|[Visual Navigation](https://github.com/v-nav/clover/blob/v-nav_article/docs/ru/v-nav.md)|| |✕|🇷🇺 Джедаи 1581|[Ретранслятор на базе Клевера](https://github.com/JJNIK/clover/blob/patch-1/docs/ru/1581.md)|| @@ -41,6 +51,8 @@ CopterHack 2022 — это международный конкурс по ра ✕ – команды, не дошедшие до финала. +Смотрите все оценки по критериям в [полной таблице](https://docs.google.com/spreadsheets/d/1qVoXchDbaBlbFzVCyxFZDU6pp8pvC1oXasowr56tnzc). + ## Направление "кейс компании" Команды приглашаются принять участие в работе над следующими кейсами компании: diff --git a/docs/ru/firmware.md b/docs/ru/firmware.md index 13b77404..57f43287 100644 --- a/docs/ru/firmware.md +++ b/docs/ru/firmware.md @@ -79,19 +79,19 @@ PX4 может быть собран из исходников и загруже Для это склонируйте репозиторий PX4: ```bash -git clone https://github.com/PX4/Firmware.git +git clone https://github.com/PX4/PX4-Autopilot.git ``` Выберите необходимую версию (тэг) с помощью `git checkout`. Затем соберите и загрузите прошивку: -``` -make px4fmu-v4_default upload +```bash +make px4_fmu-v4_default upload ``` -Где `px4fmu-v4_default` – требуемый вариант прошивки. +Где `px4_fmu-v4_default` – требуемый вариант прошивки. Для загрузки прошивки `v3` в Pixhawk может понадобиться команда `force_upload`: -``` -make px4fmu-v3_default force-upload +```bash +make px4_fmu-v3_default force-upload ``` diff --git a/docs/ru/frames.md b/docs/ru/frames.md index 6bc760fc..19a8b119 100644 --- a/docs/ru/frames.md +++ b/docs/ru/frames.md @@ -11,7 +11,8 @@ * `base_link` — координаты относительно квадрокоптера: схематичное изображение квадрокоптера на иллюстрации; * `body` — координаты относительно квадрокоптера без учета наклонов по тангажу и крену: красная, синяя и зеленая линии на иллюстрации; * `navigate_target` – координаты точки, в которую сейчас летит дрон (с использованием [navigate](simple_offboard.md#navigate)); -* `setpoint` – текущий setpoint по позиции. +* `setpoint` – текущий setpoint по позиции; +* `main_camera_optical` – система координат, [связанная с основной камерой](camera_setup.md#frame). При использовании [системы позиционирования по ArUco-маркерам](aruco.md) появляются дополнительные фреймы: diff --git a/docs/ru/lane_control.md b/docs/ru/lane_control.md new file mode 100644 index 00000000..31fba207 --- /dev/null +++ b/docs/ru/lane_control.md @@ -0,0 +1,338 @@ +# Контроль соблюдения ПДД на выделенной полосе с дроном + +## Автор + +- [Смирнов Даниил](https://github.com/londrwus), Telegram: [londrwus](https://t.me/londrwus). + +## Введение + +Автобусная полоса — это часть дороги только для пассажирского транспорта. Выделяют ее специально, чтобы горожане без машин быстрее добирались на работу и обратно домой в час пик. Благодаря выделенным полосам общественный транспорт не стоит в дорожной пробке и удобен для пассажиров. Если водитель обычной легковушки объедет по ней пробку, он получит штраф за выделенную полосу для общественного транспорта + +## Соблюдение контроля полосы + +На данный момент, контроль соблюдения полосы происходит с стационарных камер дорожного движения. Однако, не на всех участках дорог можно расположить подобного вида камеры или камеры могут не работать или вообще не выделять автобусную полосу. Поэтому было создано решения контроля полосы с помощью беспилотника. + +## Объяснение кода. Написание + +Подключаем библиотеки: + +```python +import rospy +import cv2 #OpenCV +from sensor_msgs.msg import Image +from cv_bridge import CvBridge +from std_srvs.srv import Trigger +from pyzbar import pyzbar +import numpy as np +``` + +Создаём некоторые переменные: + +```python +rospy.init_node('flight') +bridge = CvBridge() +dict_flag = [] +detect_flag = True +i = -1 +``` + +Для реализации контроля полосы и нахождения машин, автобусов, нам понадобится библиотека OpenCV. Создаём скриншот с изображением с основной камеры для обработки с использованием OpenCV: + +```python +def lane_control(): + global dict_flag, i, detect_flag, detected_blue_bus + cv_image = bridge.imgmsg_to_cv2(rospy.wait_for_message('main_camera/image_raw', Image), 'bgr8') + filtered_image = cv2.cvtColor(cv_image, cv2.COLOR_HSV2BGR) +``` + +На данный момент, автобусы производятся в синем цвете. Поэтому, автобус у нас будет синего цвета. + + + +Прописываем диапазоны цветов для автобуса и машины: + +```python +# Автобус +lower_blue = np.array([90, 60, 100]) +upper_blue = np.array([119, 255, 255]) +# Другие машины, допустим, цвет red +lower_red = np.array([160, 20, 50]) +upper_red = np.array([190, 255, 255]) +``` + +Данный скрипт требует больших вычислительных мощностей Raspberry Pi. Для ограничения работы скрипта, мы замедляем работу камеры до частоты 10 Гц (`main_camera.launch`): + +```xml + +``` + +Топик для камеры в этом случае необходимо поменять на `main_camera/image_raw_throttled` c `main_camera/image_raw`. + +```python +# car +frame = cv2.inRange(filtered_image, lower_red, upper_red) +cnt = cv2.findContours(frame, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) +try: + for c in cnt: + moments = cv2.moments(c, 1) + sum_pixel = moments['m01'] + + if sum_pixel >= 100000: + dict_flag[i][1] = [sum_pixel, int(moments['m10'] / sum_pixel), int(moments['m01'] / sum_pixel)] + detected_red_car = True + cv2.drawContours(image_src, [c], 0, (0,255,255), 6) +except: + pass + +# bus +frame = cv2.inRange(filtered_image, lower_blue, upper_blue) +cnt = cv2.findContours(frame, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) +try: + for c in cnt: + moments = cv2.moments(c, 1) + sum_pixel = moments['m01'] + + if sum_pixel >= 100000: + dict_flag[i][0] = [sum_pixel, int(moments['m10'] / sum_pixel), int(moments['m01'] / sum_pixel)] + detected_blue_bus = True + cv2.drawContours(image_src, [c], 0, (0,255,0), 10) +except: + pass + +if detected_red_car == True: + print("[DEBUG] detected car") +else: + print("[DEBUG] No cars") +``` + +Данная программа будет определять машины по её оттенку. Вот что у нас в итоге получилось. + + + +Вот примеры других цветовых диапазонов. Стоит подметить, что цветовые диапазоны вычисляются в HSV, а не в RGB: + +```python +# Красный в одном диапазоне +red_low1 = np.array([0, 40, 20]) +red_high1 = np.array([14, 255, 255]) +# Красный в другом +red_low2 = np.array([165, 40, 20]) +red_high2 = np.array([180, 255, 255]) +# Голубой +lower_blue = np.array([90, 100, 31]) +upper_blue = np.array([119, 255, 190]) +# Желтый +lower_yellow = np.array([17, 55, 55]) +upper_yellow = np.array([32, 200, 200]) +#... +``` + +Иногда надо использовать два значения HSV красного, т. к. они попадают под начало и конец диапазона HSV. + +Данная программа будет только обнаруживать, выдавать отчет об объекте, рисовать контуры объектов. Если же мы захотим, чтобы выводить на LED-ленту нарушило ли ПДД данный объект, то можно добавить переменные и добавить функцию: + +```python +Led = { "red": [255, 0, 0], "blue": [0, 0, 255] } +count_led = 72 #Если же на вашей LED-ленте 72 светодиода, если 58 - ставьте значение 58 +Led_full = [0]*count_led +def led_print(): + global count_led, Led_full + set_effect(r=0, g=0, b=0) + rospy.sleep(4) + a = [] + for i in range(count_led): + a.append(LEDState(i, Led[Led_full[i][0]][0], Led[Led_full[i][0]][1], Led[Led_full[i][0]][2])) + set_leds(a) +``` + +Дополним функцию `lane_control`: + +```python +S = sum([ dict_flag[i][0] for j in range (len(dict_flag[i]))]) +j, k = 0, 0 +print(dict_flag[i][0][0]) +for k in range(int(dict_flag[i][0][0]/S * count_led)): + Led_full[k+j] = ["red", dict_flag[i][0][1], dict_flag[i][0][2]] +j+=k +k = 0 +print(j, k ) +for k in range(int(dict_flag[i][1][0]/S * count_led)): + Led_full[k+j] = ["blue", dict_flag[i][1][1], dict_flag[i][1][2]] +j+=k +k = 0 +print(j, k) + +if abs(Led_full[0][1] - Led_full[count_led/2][1]) > 50: + Led_full.sort(key=lambda Led_full: Led_full[1]) # сортировка по x +else: + Led_full.sort(key=lambda Led_full: Led_full[2]) # сортировка по y + +print(Led_full) +``` + +После этого наша светодиодная лента будет обозначать нарушил ли данный объект ПДД или нет. + +Но даже после такого количества кода, наш дрон не будет следовать за нарушителем. Давайте сделаем так, чтобы дрон мог преследовать нарушителя постоянно, чтобы он не мог скрыться с места нарушения. Создадим базовые значения + +```python +# Библиотеки +import tf +import tf2_ros +import geometry_msgs.msg +import tf2_geometry_msgs +import numpy as np +from geometry_msgs.msg import Vector3Stamped, Point, PointStamped + +ar = "aruco_13" # ID нарушителя +tfBuffer = tf2_ros.Buffer() +listener = tf2_ros.TransformListener(tfBuffer) + +l_z = 2 # Для будущего кода дальномера + +def range_callback(msg): + global l_z + l_z = msg.range # Вывод Z по дальномеру + +rospy.Subscriber('rangefinder/range', Range, range_callback) + +def get_aruco_pose(frame_id): # Возвращает координаты, с помощью преобразование нового кадра + global tfBuffer, listener + try: + trans = tfBuffer.lookup_transform(frame_id, ar, rospy.Time()) + except: + return None + pnt_l0 = tf2_geometry_msgs.do_transform_point(PointStamped(point=Point(x=0, y=0, z=0)), trans) + l0 = np.array([pnt_l0.point.x, pnt_l0.point.y, pnt_l0.point.z]) + return l0 + +def get_body_pose(frame_id): # Возвращает координаты, с помощью преобразование нового кадра + global tfBuffer, listener + try: + trans = tfBuffer.lookup_transform(frame_id, "body", rospy.Time()) + except: + return None + pnt_l0 = tf2_geometry_msgs.do_transform_point(PointStamped(point=Point(x=0, y=0, z=0)), trans) + l0 = np.array([pnt_l0.point.x, pnt_l0.point.y, pnt_l0.point.z]) + return l0 + +def remove_0_vel(vel): # Скорости полета в POSITION + if np.linalg.norm(vel[:2]) < 0.045: + vel[0] = 0 + vel[1] = 0 + return vel +``` + +Создадим функцию, которая будет производить весь этот полет. Для начала сделаем так, чтобы дрон находил нарушителя. + +```python +def follow_violator(): + z = 1.5 + + navigate_wait(z=1.5, speed=1, frame_id="body", auto_arm = True) + rospy.sleep(2) + set_effect(r=255, g=255, b=0) + print("[DEBUG] started navigation") + navigate_wait(x=0, y=0, z=z, speed=0.8, frame_id=ar, yaw=float('nan'), tolerance=0.2) +``` + +В итоге, мы написали код, но давайте сделаем так, чтобы он постоянно двигался за ним и улучшал его позиционирование. + +```python + FRQ = 5 + r = rospy.Rate(FRQ) + prev_vel = None + prev_pa = None + prev_t = rospy.get_time() + st_t = rospy.get_time() + d = 10 + # Вычисляет на сколько надо перемещаться, за счет изменения кадров (прошлого и нынешнего) + while d > 1 or (rospy.get_time() - st_t < 0.5): + pb = get_body_pose("aruco_map") + pa = get_aruco_pose("aruco_map") + now = rospy.get_time() + if prev_pa is None: + if pb is None: + r.sleep() + continue + navigate(x=pb[0], y=pb[1], z=z, speed=1, frame_id="aruco_map") # Снижение высоты к подлету к маркеру + set_effect(r=150, g=0, b=255, effect='blink') + prev_pa = pa + prev_t = now + else: + if pb is not None: + d = np.linalg.norm(pb[:1]-pa[:1]) + if pa is not None: + set_effect(r=150, g=0, b=255) + vel = (pa-prev_pa)/(now-prev_t+0.0001) + vel = np.clip(vel, -0.7, 0.7) + + vel = remove_0_vel(vel) + if prev_vel is not None: + vel = vel*0.1 + prev_vel*0.1 + + t = pa[:1] + vel[:1]*(0.1/FRQ)*2.7 + set_position(x=t[0], y=t[1], z=z, frame_id="aruco_map") + prev_pa = pa.copy() + prev_vel = vel.copy() + prev_t = now + else: + navigate(x=pb[0], y=pb[1], z=z, frame_id="aruco_map") + set_effect(r=150, g=0, b=255, effect='blink_fast') + print("NO pa and vel") + + r.sleep() + + set_effect(r=255, g=150, b=0) + z_st = 1.5 + + st_t = rospy.get_time() + z_vel = 0.35 + + Z = z_st + r = rospy.Rate(20) + # Вычисляет на сколько надо изменить координату Z, за счет изменения кадров (прошлого и нынешнего) и времени + while l_z > 0.09: + pb = get_body_pose("aruco_map") + pa = get_aruco_pose("aruco_map") + now = rospy.get_time() + if prev_pa is None: + set_position(x=0, y=0, z=-0.01, frame_id="body") + set_effect(r=255, g=150, b=0, effect='blink_fast') + print("NO prev_pa") + prev_pa = pa + prev_t = now + else: + if pb is not None: + d = np.linalg.norm(pb[:1]-pa[:1]) + if pa is not None: + set_effect(r=255, g=150, b=0) + if np.linalg.norm(pa - prev_pa) < 0.0001 and prev_vel is not None: + vel = prev_vel.copy()*0.97 + else: + vel = (pa-prev_pa)/(now-prev_t+0.0001) + if l_z > 0.5: + vel = np.clip(vel, -0.7, 0.7) + else: + vel = np.clip(vel, -0.5, 0.5) + + vel = remove_0_vel(vel) + if np.linalg.norm(vel[:1]) < 0.02 and d <= 0.07: + Z = -(rospy.get_time()-st_t)*(0.1) + z_st + else: + Z = -(rospy.get_time()-st_t)*z_vel + z_st + + t = pa[:1] + vel[:1]*(0.1/FRQ)*2.2 + set_position(x=t[0], y=t[1], z=Z, frame_id="aruco_map") + prev_pa = pa.copy() + print("Z = ", Z) + prev_vel = vel.copy() + prev_t = now + else: + set_position(x=0, y=0, z=-0.01, frame_id="body") + set_effect(r=255, g=150, b=0, effect='blink_fast') + print("NO pa and vel") + + r.sleep() +``` + +Вывод: мы написали довольно короткий, для поставленных перед ним задач, код. Данный код фиксирует правонарушение, регулирует вариативность определения транспортного средства и совершает преследование нарушителя diff --git a/docs/ru/laser.md b/docs/ru/laser.md index 8979c652..2b401c35 100644 --- a/docs/ru/laser.md +++ b/docs/ru/laser.md @@ -75,7 +75,7 @@ from sensor_msgs.msg import Range # ... -data = rospy.wait_for_message('rangefinder/range', Range) +dist = rospy.wait_for_message('rangefinder/range', Range).range ``` ### Визуализация данных diff --git a/docs/ru/leds.md b/docs/ru/leds.md index 3223f512..5519c2cb 100644 --- a/docs/ru/leds.md +++ b/docs/ru/leds.md @@ -158,3 +158,9 @@ rosservice call /led/set_leds "leds: ```bash rostopic echo /led/state ``` + +Используя этот же топик можно получить общее выставленное в настройках количество светодиодов: + +```python +led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds) +``` diff --git a/docs/ru/magnetic_grip.md b/docs/ru/magnetic_grip.md index 5582b45e..c9251174 100644 --- a/docs/ru/magnetic_grip.md +++ b/docs/ru/magnetic_grip.md @@ -63,3 +63,64 @@ 2. Стяжкой притяните собранную схему к обратной стороне деки. 3. Сигнальный вывод Arduino *D11* вставьте в один из выводов *AUX* на полетном контроллере. 4. Вставьте силовой вывод электромагнитного захвата в JST 5В. + +## Настройка электромагнитного захвата + +Для управления магнитом через плату Arduino Nano, используйте код ниже: + +```cpp +void setup() { + pinMode(11, INPUT); + pinMode(13, OUTPUT); +} + +void loop() { + if (int duration = pulseIn(11, HIGH) > 1200) { + digitalWrite(13, HIGH); + } else { + digitalWrite(13, LOW); + } +} +``` + +Для однозначного определения статуса магнитного захвата, можно подключить светодиодную ленту типа *ws281x* (входит в наборы "Клевер"). Подключите ее к питанию +5v – 5v, земле GND – GND и сигнальный контакт DIN – Arduino D12. + +Для управления магнитным захватом со светодиодной лентой через плату Arduino Nano используйте код ниже: + +```cpp +#include +#define NUMPIXELS 72 +#define PIN 12 +int pin = 11; +int led = 13; + +unsigned long duration; +Adafruit_NeoPixel strip (NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); + +void setup() { + strip.begin(); + strip.setBrightness(10); + Serial.begin(9600); + pinMode(pin, INPUT); + pinMode(led, OUTPUT); +} + +void loop() { + duration = pulseIn(pin, HIGH); + Serial.println(duration); + delay(100); + if (duration >= 1500) { + digitalWrite(led, HIGH); + for (int i = -1; i < NUMPIXELS; i++) { + strip.setPixelColor(i, strip.Color(255, 0, 0)); + strip.show(); + } + } else { + digitalWrite(led, LOW); + for (int i = -1; i < NUMPIXELS; i++) { + strip.setPixelColor(i, strip.Color(0, 255, 0)); + strip.show(); + } + } +} +``` diff --git a/docs/ru/optical_flow.md b/docs/ru/optical_flow.md index 99f21e7f..86eef625 100644 --- a/docs/ru/optical_flow.md +++ b/docs/ru/optical_flow.md @@ -29,7 +29,7 @@ Optical Flow публикует данные в топик `/mavros/px4flow/raw/ * `LPE_FLW_RR` – 0.0. * `SENS_FLOW_ROT` – No rotation (отсутствие поворота). * `SENS_FLOW_MAXHGT` – 4.0 (для дальномера VL53L1X) -* `SENS_FLOW_MINHGT` – 0.01 (для дальномера VL53L1X) +* `SENS_FLOW_MINHGT` – 0.0 (для дальномера VL53L1X) * Опционально: `LPE_FUSION` – включен флажок pub agl as lpos down (см. [конфигурирование дальномера](laser.md). При использовании **EKF2** (параметр `SYS_MC_EST_GROUP` = `ekf2`): @@ -41,7 +41,7 @@ Optical Flow публикует данные в топик `/mavros/px4flow/raw/ * `EKF2_OF_N_MAX` - 0.2. * `SENS_FLOW_ROT` – No rotation (отсутствие поворота). * `SENS_FLOW_MAXHGT` – 4.0 (для дальномера VL53L1X) -* `SENS_FLOW_MINHGT` – 0.01 (для дальномера VL53L1X) +* `SENS_FLOW_MINHGT` – 0.0 (для дальномера VL53L1X) * Опционально: `EKF2_HGT_MODE` – range sensor (см. [конфигурирование дальномера](laser.md)). Для проверки правильности всех настроек можно [воспользоваться утилитой `selfcheck.py`](selfcheck.md). diff --git a/docs/ru/parameters.md b/docs/ru/parameters.md index f5a678ed..312de3e0 100644 --- a/docs/ru/parameters.md +++ b/docs/ru/parameters.md @@ -1,20 +1,64 @@ # Параметры PX4 -Основная статья: https://dev.px4.io/en/advanced/parameter_reference.html +Полная документация по параметрам PX4: https://docs.px4.io/master/en/advanced_config/parameter_reference.html. -> **Note** Это описание некоторых, наиболее важных параметров PX4 по состоянию на версию 1.8.0. Полный список см. по ссылке выше. +Для изменения параметров PX4 используйте программу QGroundControl, [подключившись к Клеверу по Wi-Fi](gcs_bridge.md) или USB. Перейдите в панель *Vehicle Setup* (кликнув на логотип QGroundControl в левом верхнем углу и выберите меню *Parameters*. -Для изменения параметров PX4 можно воспользоваться программой QGroundControl, [подключившись к Клеверу по Wi-Fi](gcs_bridge.md): +## Рекомендованные значения -![Параметры PX4 в QGroundControl](../assets/qgc-params.png) +### Общие параметры -## Основные параметры +|Параметр|Значение|Примечание| +|-|-|-| +|`SENS_FLOW_ROT`|0 (*No rotation*)|В случае использования "железного" [PX4Flow](px4flow.md), оставьте значение по умолчанию| +|`SENS_FLOW_MINHGT`|0.0|Для [дальномера VL53L1X](laser.md)| +|`SENS_FLOW_MAXHGT`|4.0|Для [дальномера VL53L1X](laser.md)| +|`SENS_FLOW_MAXR`|10.0|| +|`SYS_HAS_MAG`|0|При невозможности запуска магнитометра (ошибка *No mags found*)| -Наиболее важные параметры вынесены в этот параграф. +### Настройки подсистемы Estimator -`SYS_MC_EST_GROUP` – выбор модуля estimator'а. +В случае использования LPE ([прошивка COEX](firmware.md)): -Это группа модулей, которая вычисляет текущее состояние (state) коптера, используя показания с датчиков. В состояние коптера входит: +|Параметр|Значение|Примечание| +|-|-|-| +|`LPE_FUSION`|86|Чекбоксы: *flow* + *vis* + *land detector* + *gyro comp*. При полете над ровным полом возможно включение *pub agl as lpos down*.
Подробнее: [Optical Flow](optical_flow.md), [ArUco-маркеры](aruco_map.md), [GPS](gps.md).| +|`LPE_VIS_DELAY`|0.0|| +|`LPE_VIS_Z`|0.1|| +|`LPE_FLW_SCALE`|1.0|| +|`LPE_FLW_R`|0.2|| +|`LPE_FLW_RR`|0.0|| +|`LPE_FLW_QMIN`|10|| +|`ATT_W_EXT_HDG`|0.5|Включение использования внешнего угла по рысканью (при навигации по [карте маркеров](aruco_map.md))| +|`ATT_EXT_HDG_M`|1 (*Vision*)|| +|`ATT_W_MAG`|0|Выключение магнитометра (при навигации внутри помещения)| + +В случае использования EKF2 (официальная прошивка): + + + +|Параметр|Значение|Примечание| +|-|-|-| +|`EKF2_AID_MASK`|26|Чекбоксы: *flow* + *vision position* + *vision yaw*.
Подробнее: [Optical Flow](optical_flow.md), [ArUco-маркеры](aruco_map.md), [GPS](gps.md).| +|`EKF2_OF_DELAY`|0|| +|`EKF2_OF_QMIN`|10|| +|`EKF2_OF_N_MIN`|0.05|| +|`EKF2_OF_N_MAX`|0.2|| +|`EKF2_HGT_MODE`|2 (*Range sensor*)|При наличии [дальномера](laser.md) и полете над ровным полом| +|`EKF2_EVA_NOISE`|0.1|| +|`EKF2_EVP_NOISE`|0.1|| +|`EKF2_EV_DELAY`|0|| +|`EKF2_MAG_TYPE`|5 (*None*)|Выключение магнитометра (при навигации внутри помещения)| + + + +> **Info** См. также: список параметров по умолчанию в [симуляторе](simulation.md): https://github.com/CopterExpress/clover/blob/master/clover_simulation/airframes/4500_clover. + +## Дополнительная информация + +Параметр `SYS_MC_EST_GROUP` отвечает за выбор Estimator'а. + +Estimator это подсистема, которая вычисляет текущее состояние (state) коптера, используя показания с датчиков. В состояние коптера входит: * угловая скорость коптера – pitch_rate, roll_rate, yaw_rate; * ориентация коптера (в локальной системе координат) – pitch (тангаж), roll (крен), yaw (рысканье) (одно из представлений); @@ -57,9 +101,7 @@ ## LPE + Q attitude estimator -Данные параметры настраивают поведение модулей `lpe` и `q`, которые вычисляют состояние (ориентацию, позицию) коптера. Эти параметры применяются **только** если параметр `SYS_MC_EST_GROUP` установлен в значение `1` (local_position_estimator, attitude_estimator_q) - -TODO +Данные параметры настраивают поведение модулей `lpe` и `q`, которые вычисляют состояние (ориентацию, позицию) коптера. Эти параметры применяются **только** если параметр `SYS_MC_EST_GROUP` установлен в значение `1` (local_position_estimator, attitude_estimator_q). ## Commander @@ -68,5 +110,3 @@ TODO ## Sensors Включение, выключение и настройка различных датчиков. - -TODO diff --git a/docs/ru/rviz.md b/docs/ru/rviz.md index d60bbc2d..019814dc 100644 --- a/docs/ru/rviz.md +++ b/docs/ru/rviz.md @@ -11,7 +11,7 @@ > **Hint** Вы можете можете использовать готовый [образ виртуальной машины](simulation_vm.md) с инструментами для Клевера. -На него необходимо установить пакет `ros-melodic-desktop-full` или `ros-melodic-desktop`, используя [документацию по установке](http://wiki.ros.org/melodic/Installation/Ubuntu). +На него необходимо установить пакет `ros-noetic-desktop-full` или `ros-noetic-desktop`, используя [документацию по установке](http://wiki.ros.org/noetic/Installation/Ubuntu). Запуск rviz --- diff --git a/docs/ru/setup.md b/docs/ru/setup.md index 9562dba6..a1a3053c 100644 --- a/docs/ru/setup.md +++ b/docs/ru/setup.md @@ -16,39 +16,32 @@ Pixracer и MicroSD-карта -* Установите карту в компьютер (используйте адаптер при необходимости). -* Отформатируйте карту в файловую систему FAT32. Для этого кликните на значок SD-карты в "Проводнике" и нажмите "Форматирование" в Windows. Используйте "Дисковую утилиту" в macOS. -* Выполните "Безопасное извлечение" карты, извлеките карту. -* Установите карту в полетный контроллер. +1. Установите карту в компьютер (используйте адаптер при необходимости). +2. Отформатируйте карту в файловую систему FAT32. Для этого кликните на значок SD-карты в "Проводнике" и нажмите "Форматирование" в Windows. Используйте "Дисковую утилиту" в macOS. +3. Выполните "Безопасное извлечение" карты, извлеките карту. +4. Установите карту в полетный контроллер. ## Загрузка прошивки в полетный контроллер -Основная статья: https://docs.qgroundcontrol.com/en/SetupView/Firmware.html. +Наиболее оттестированной, в особенности для осуществления автономных полетов, является [версия прошивки с патчами COEX](firmware.md). Скачайте актуальную версию прошивки на GitHub — **скачать**. -> **Note** Перед осуществлением перепрошивки Pixracer не должен быть подключен к компьютеру по USB. +Для использования всех наиболее актуальных функций PX4 вы также можете использовать последнюю официальную версию прошивки (в экспериментальном режиме). -Для Клевера, в особенности для осуществления автономных полетов, рекомендуется использовать версию прошивки PX4 от Copter Express. Скачайте актуальную версию прошивки на GitHub — **скачать**. +1. Отключите полетный контроллер от компьютера (если он подключен). +2. Запустите программу QGroundControl. +3. Перейдите в панель *Vehicle Setup* (кликнув на логотип QGroundControl в левом верхнем углу) и выберите меню *Firmware*. +4. Подключите полетный контроллер к компьютеру по USB. +5. Выберите в появившемся меню справа *PX4 Flight Stack*. -> **Info** Для квадрокоптеров с Pixhawk (Клевер 2) существует отдельная версия прошивки. Подробности смотрите в статье "[Прошивка полетного контроллера](firmware.md)". + QGroundControl firmware upload -Загрузите прошивку в полетный контролер: +6. Для загрузки **прошивки COEX**: -QGroundControl firmware upload + * Выберите *Advanced settings*. + * В выпадающем меню выберите *Custom firmware file...* + * Нажмите *OK* и выберите скаченный файл прошивки. -1. Запустите программу QGroundControl. -2. Зайдите во вкладку *Vehicle Setup*. -3. Выберите меню *Firmware*. -4. Подключите Pixracer к компьютеру по USB. -5. Дождитесь подключения Pixracer к QGroundControl. -6. Выберите в меню справа *PX4 Flight Stack*. - -Для загрузки прошивки от Copter Express (рекомендуется): - -* Выберите *Advanced settings*. -* В выпадающем меню выберите *Custom firmware file...* -* Нажмите *OK* и выберите скаченный файл прошивки. - -Для загрузки последней версии стандартной прошивки сразу нажмите *OK*. + Для загрузки последней версии **стандартной прошивки** сразу нажмите *OK*. Дождитесь, пока QGroundControl загрузит прошивку и выполнит перезагрузку полетного контроллера. @@ -82,7 +75,7 @@ ### Параметры -Для настройки параметров полетного контроллера войдите во вкладку *Vehicle Setup* и выберите меню *Parameters*. Вы можете использовать поле *Search* для поиска параметров по имени. +Для настройки параметров полетного контроллера войдите во вкладку *Vehicle Setup* и выберите меню *Parameters*. Вы можете использовать поле *Search* для поиска параметров по имени. Рекомендуемые параметры для Клевера приведены в дальнейшей документации а также в соответствующей [сводной статье](parameters.md). QGroundControl parameters diff --git a/docs/ru/simple_offboard.md b/docs/ru/simple_offboard.md index 42502441..29938e25 100644 --- a/docs/ru/simple_offboard.md +++ b/docs/ru/simple_offboard.md @@ -1,11 +1,4 @@ -Автономный полет (OFFBOARD) -=== - -> **Note** В версии образа **0.20** пакет `clever` был переименован в `clover`. Для более ранних версий см. документацию для версии [**0.19**](https://github.com/CopterExpress/clover/blob/v0.19/docs/ru/simple_offboard.md). - - - -> **Hint** Для автономных полетов рекомендуется использование [специальной сборки PX4 для Клевера](firmware.md#прошивка-для-клевера). +# Автономный полет Модуль `simple_offboard` пакета `clover` предназначен для упрощенного программирования автономного полета дрона ([режим](modes.md) `OFFBOARD`). Он позволяет устанавливать желаемые полетные задачи и автоматически трансформирует [систему координат](frames.md). @@ -13,8 +6,7 @@ Основные сервисы – [`get_telemetry`](#gettelemetry) (получение телеметрии), [`navigate`](#navigate) (полет в заданную точку по прямой), [`navigate_global`](#navigateglobal) (полет в глобальную точку по прямой), [`land`](#land) (переход в режим посадки). -Использование из языка Python ---- +## Использование из языка Python Для использования сервисов, необходимо создать объекты-прокси к ним. Используйте этот шаблон для вашей программы: @@ -37,8 +29,7 @@ land = rospy.ServiceProxy('land', Trigger) Неиспользуемые функции-прокси можно удалить из кода. -Описание API ---- +## Описание API > **Note** Незаполненные числовые параметры устанавливаются в значение 0. @@ -114,7 +105,7 @@ rosservice call /get_telemetry "{frame_id: ''}" * `auto_arm` – перевести коптер в `OFFBOARD` и заармить автоматически (**коптер взлетит**); * `frame_id` – [система координат](frames.md), в которой заданы `x`, `y`, `z` и `yaw` (по умолчанию: `map`). -> **Note** Для полета без изменения угла по рысканью достаточно установить `yaw` в `NaN` (значение угловой скорости по-умолчанию – 0). +> **Note** Для полета без изменения угла по рысканью достаточно установить `yaw` в `NaN` (значение угловой скорости по умолчанию – 0). Взлет на высоту 1.5 м со скоростью взлета 0.5 м/с: @@ -192,7 +183,7 @@ rosservice call /navigate "{x: 0.0, y: 0.0, z: 2, yaw: 0.0, yaw_rate: 0.0, speed * `auto_arm` – перевести коптер в `OFFBOARD` и заармить автоматически (**коптер взлетит**); * `frame_id` – [система координат](frames.md), в которой заданы `z` и `yaw` (по умолчанию: `map`). -> **Note** Для полета без изменения угла по рысканью достаточно установить `yaw` в `NaN` (значение угловой скорости по-умолчанию – 0). +> **Note** Для полета без изменения угла по рысканью достаточно установить `yaw` в `NaN` (значение угловой скорости по умолчанию – 0). Полет в глобальную точку со скоростью 5 м/с, оставаясь на текущей высоте (`yaw` установится в 0, коптер сориентируется передом на восток): @@ -312,14 +303,9 @@ if res.success: rosservice call /land "{}" ``` - - -Дополнительные материалы ------------------------- +## Дополнительные материалы * [Полеты в поле ArUco-маркеров](aruco.md). * [Примеры программ и сниппеты](snippets.md). diff --git a/docs/ru/simulation_m1.md b/docs/ru/simulation_m1.md new file mode 100644 index 00000000..714a6bdd --- /dev/null +++ b/docs/ru/simulation_m1.md @@ -0,0 +1,56 @@ +# Симулятор на компьютерах с чипом M1 + +Для архитектуры ARM64, которую используют компьютеры с чипом M1 (Apple Silicon), [готовый образ с симулятором](simulation_vm.md) не выпускается, поэтому возможна только ручная установка симулятора. + +В качестве виртуальной машины рекомендуется использовать бесплатное приложение [**UTM**](https://mac.getutm.app/). Также возможно использование **VMware Fusion Public Tech Preview** с поддержкой M1. + +## Установка симулятора с UTM + + + +1. Скачайте UTM с официального сайта [mac.getutm.app](https://mac.getutm.app/) и установите его. +2. Скачайте исходный образ установщика Ubuntu 20.04 для архитектуры ARM64 по ссылке: https://cdimage.ubuntu.com/focal/daily-live/current/focal-desktop-arm64.iso. +3. Создайте новую виртуальную машину в UTM, выбирая следующие настройки: + + * **Тип**: Virtualize. + * **Operating System** (ОС): Linux. + * **Boot ISO Image** (образ для загрузки): выберите скаченный образ `focal-desktop-arm64.iso`. + * **Memory** (память): 4096 MB или более. + * **CPU Cores** (ядра процессора): 4 или более. + * Включите поддержку OpenGL: *Enable hardware OpenGL acceleration*. + * **Storage** (размер хранилища): 20 GB или более. + +4. Запустите созданную виртуальную машину. +5. Выберите пункт *Install Ubuntu* и установите Ubuntu с помощью мастера установки. + + * Рекомендуемый набор ПО: *Minimal installation*. + * Тип установки: *Erase disk and install Ubuntu*. + * Введите параметры учетной записи по желанию, например: + + + +6. Завершите установку и запустите установленную систему. +7. Установите симулятор согласно [инструкции по сборке симулятора на собственной машине](simulation_native.md). + +### Возможные проблемы при установке + +#### Отсутствие картинки + +Если при запуске виртуальной машины вместо изображения вы видите черный фон, попробуйте запустить машину с отключенной поддержкой GPU. + +В настройках виртуальной машины выберите *Display*, в пункте *Emulated Display Card* выберите *virtio-ramfb*. Запустите машину. При успешном запуске поменяйте настройку обратно на *virtio-ramfb-gl (GPU Supported)* и снова запустите машину. + +#### Проблема с `git clone` + +При осуществлении команды `git clone` может возникнуть подобная ошибка: + +```txt +on git clone if error: RPC failed; curl 56 GnuTLS recv error (-54): Error in the pull function. +fatal: the remote end hung up unexpectedly +fatal: early EOF +fatal: index-pack failed +``` + +В этом случае поменяйте типа сетевой карты на Bridged. В настройках виртуальной машины выберите *Network*, в пункте *Network Mode* выберите *Bridged (Advanced)*. + +В дальнейшем, при возникновении проблем с сетью измените тип сети обратно на *Shared Network*. diff --git a/docs/ru/simulation_native.md b/docs/ru/simulation_native.md index d9a7c1b4..90573756 100644 --- a/docs/ru/simulation_native.md +++ b/docs/ru/simulation_native.md @@ -132,6 +132,12 @@ roslaunch clover_simulation simulator.launch ## Дополнительные шаги +Для того, чтобы возможно было запускать среду симуляции Gazebo отдельно (команда `gazebo`), добавьте в `.bashrc` вызов соответствующего скрипта инициализации: + +```bash +echo "source /usr/share/gazebo/setup.sh" >> ~/.bashrc +``` + Опционально вы можете установить systemd-сервис для roscore для того, чтобы roscore был постоянно запущен в фоне: ```bash @@ -144,10 +150,10 @@ sudo systemctl start roscore Установите любой веб-сервер, чтобы раздавать веб-инструменты Клевера (директория `~/.ros/www`), например, Monkey: ```bash -wget https://github.com/CopterExpress/clover_vm/raw/master/assets/packages/monkey_1.6.9-1_amd64.deb -O /tmp/monkey_1.6.9-1_amd64.deb -sudo apt-get install -y /tmp/monkey_1.6.9-1_amd64.deb +wget https://github.com/CopterExpress/clover_vm/raw/master/assets/packages/monkey_1.6.9-1_$(dpkg --print-architecture).deb -P /tmp +sudo dpkg -i /tmp/monkey_*.deb sed "s/pi/$USER/g" ~/catkin_ws/src/clover/builder/assets/monkey | sudo tee /etc/monkey/sites/default -sudo -E sh -c "sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf" +sudo sed -i 's/SymLink Off/SymLink On/' /etc/monkey/monkey.conf sudo cp ~/catkin_ws/src/clover/builder/assets/monkey.service /etc/systemd/system/monkey.service sudo systemctl enable monkey sudo systemctl start monkey diff --git a/docs/ru/simulation_usage.md b/docs/ru/simulation_usage.md index aee32d09..591862b3 100644 --- a/docs/ru/simulation_usage.md +++ b/docs/ru/simulation_usage.md @@ -42,6 +42,8 @@ GPS датчик необходим полетов с использование ``` +Включите также флажок *use GPS* в параметре PX4 `EKF2_AID_MASK` (используя QGroundControl). + ### Камера При полете по GPS камера может быть не нужна, ее также можно отключить в файле `simulator.launch`: diff --git a/docs/ru/simulation_vm.md b/docs/ru/simulation_vm.md index 29832e89..3ea08464 100644 --- a/docs/ru/simulation_vm.md +++ b/docs/ru/simulation_vm.md @@ -1,10 +1,10 @@ # Установка виртуальной машины -Для работы с платформой Клевер рекомендуется иметь [установленное окружение ROS](ros.md) на своём компьютере. К сожалению, [установка ROS](ros-install.md) сопряжена с рядом трудностей: требуется использовать операционную систему Ubuntu 18.04, процесс установки длительный и требует выполнения большого количества команд в терминале. +Для работы с платформой Клевер рекомендуется иметь [установленное окружение ROS](ros.md) на своём компьютере. К сожалению, [установка ROS и симулятора](simulation_native.md) сопряжена с рядом трудностей: требуется использовать операционную систему Ubuntu 20.04, процесс установки длительный и требует выполнения большого количества команд в терминале. Для облегчения процесса настройки окружения мы предлагаем использовать виртуальную машину со всем необходимым для работы с платформой Клевер. В состав виртуальной машины входят: -* операционная система Ubuntu 18.04 с легковесной графической оболочкой XFCE; +* операционная система Ubuntu 20.04 с легковесной графической оболочкой XFCE; * предустановленные пакеты ROS для работы с Клевером; * QGroundControl; * предварительно настроенный симулятор Gazebo; @@ -18,8 +18,6 @@ Скачать текущую версию виртуальной машины можно [в релизах репозитория виртуальной машины](https://github.com/CopterExpress/clover_vm/releases/latest). -> **Warning** Виртуальную машину следует использовать только в тех случаях, когда по каким-то причинам использование Ubuntu 18.04 напрямую невозможно. Производительность всех программ, особенно тех, которые используют 3D-графику - jMAVSim, Gazebo, rviz - будет существенно ниже; кроме того, в ряде случаев будут возникать графические ошибки, приводящие к частичной или полной неработоспособности указанных программ. - ## Установка виртуальной машины Для запуска виртуальной машины разработчика требуется использовать одну из совместимых сред виртуализации: [VirtualBox](https://www.virtualbox.org/wiki/Downloads), [VMware Player](https://www.vmware.com/products/workstation-player.html), [VMware Workstation](https://www.vmware.com/products/workstation-pro.html). diff --git a/docs/ru/snippets.md b/docs/ru/snippets.md index de2eee9b..7a9f2271 100644 --- a/docs/ru/snippets.md +++ b/docs/ru/snippets.md @@ -369,15 +369,36 @@ calibrate_gyro() import rospy import dynamic_reconfigure.client -client = dynamic_reconfigure.client.Client('aruco_detect') +rospy.init_node('flight') +aruco_client = dynamic_reconfigure.client.Client('aruco_detect') -# Включить распознавание маркеров -client.update_configuration({'enabled': False}) +# Выключить распознавание маркеров +aruco_client.update_configuration({'enabled': False}) rospy.sleep(5) -# Выключить распознавание маркеров -client.update_configuration({'enabled': True}) +# Включить распознавание маркеров +aruco_client.update_configuration({'enabled': True}) +``` + +### # {#optical-flow-enabled} + +Динамически включать и отключать [Optical Flow](optical_flow.md): + +```python +import rospy +import dynamic_reconfigure.client + +rospy.init_node('flight') +flow_client = dynamic_reconfigure.client.Client('optical_flow') + +# Выключить Optical Flow +flow_client.update_configuration({'enabled': False}) + +rospy.sleep(5) + +# Включить Optical Flow +flow_client.update_configuration({'enabled': True}) ``` ### # {#wait-global-position} diff --git a/docs/ru/testing.md b/docs/ru/testing.md new file mode 100644 index 00000000..aff01f86 --- /dev/null +++ b/docs/ru/testing.md @@ -0,0 +1,100 @@ +# Список тестирования + +Актуальный список для ручного тестирования релизов Клевера. + +Критичность: **критично**, средняя критичность, *не критично*. + +## [Образ Клевера](image.md) + +### Общие тесты + +* **Раздача [Wi-Fi](wifi.md)** +* **Возможность подключения по [SSH](ssh.md) по IP и Hostname** +* **Успешное подключение COEX Pix по USB (по умолчанию)** +* Успешное подключение COEX Pix по UART (с настройкой) +* **Бридж для QGC – корректное подключение по TCP** +* Бридж для QGC – корректное подключение по UDP-b +* **Раздача главной страницы** +* **Раздача пользовательской документации (RU/EN), отсутствие битых изображений и т. д.** +* **Работа веб-терминала Butterfly** +* **Работа web_video_server** +* **Корректная работа драйвера камеры, корректные изображения и данные в топиках** +* **Корректная работа драйвера vl53l1x (i2c к Raspberry), в том числе топика `~data`** +* **Корректная работа optical flow и всех его топиков, полет по optical flow** +* **Полет по полю маркеров** +* **Корректная установка OpenCV – возможность использования из Python и C++** +* **Отсутствие неожиданного жора памяти и CPU (можно контролировать с помощью `selfcheck.py` или `htop`)** +* Автоматическая перекалибровка камеры при изменении разрешения + +### Тесты веб-части + +* Работа веб-просмотрщика топиков +* Работа веб-консоли +* Работа веб 3D-визуализации ArUco (map, detect) +* Работа веб 3D-визуализации web_rviz +* Работа веб 3D-визуализации web_visualization_aruco_map +* Работает отображение карты ArUco `/aruco_map/image` и в snapshot, и в debug +* Визуализация расположения камеры в web rviz +* Правильное отображение осей в `/aruco_map/image` + +### Тесты selfcheck.py + +* **Корректная работа `rosrun clover selfcheck.py`, отсутствие варнингов, анализ вывода** +* **Выводит ориентацию камеры текстом** +* **Делает `commander check`** +* **Показывается, что используется наш форк прошивки и версию образа** +* **Показывает возникающие ошибки и опечатки, допущенные в .launch файлах** +* **Проверка на throttling** + +### Тесты simple_offboard + +* **Корректная работа simple_offboard – взлет, полет в точку в любом фрейме, отсутствие проблем с `yaw` и `yaw_rate`** +* **В фрейме `body`** +* **В фрейме `aruco_map`** +* **В фрейме `map`** +* **В фрейме `navigate_target`** +* Корректное выполнения флипа +* **Возможность лететь к отдельным маркерам в карте, которые вне кадра и в кадре** +* **Корректное детектирование статуса kill switch при выполнение команды с флагом `auto_arm`** +* *Корректная работа outdoor по GPS-координатам* +* Работают программы из папки `~/examples` + +### Тесты [ArUco](aruco.md) + +* **Распознавание ArUco-маркеров, корректная работа всех топиков пакета `aruco_detect` и `aruco_map`** +* **VPE-полеты по маркерам на полу** +* *VPE-полеты по маркерам на потолке* +* Корректное распознавание ArUco-маркеров и ArUco-карты (проверка с помощью rviz или debug) +* *Работает в случае если используется слишком большой ID* +* Работают комментарии в файле карты, а также в карте используется от 4 до 8 параметров +* Полет по Optical Flow над 1 маркером +* `aruco_map` не падает в случае маленьких размеров карты и маркеров + +### Тесты [pigpiod](gpio.md) + +* Корректная работа pigpiod, возможность работы с сонаром, сервой и электромагнитом по мануалу +* Одновременная работа pigpiod и rpi_ws281x (правильная работа светодиодной ленты и сервы) + +### Тесты [LED-ленты](leds.md) + +* **Работает нода LED ленты на RPi 4** +* Дополнительная проверка на RPi 3, RPi 4 Rev. 1.4 +* **Корректная работа всех notify эффектов заданных в `led.launch`** +* **Низкоуровневое управление отдельными диодами** +* **Высокоуровневое управление эффектами** + +### [Блочное программирование](blocks.md) + +* Корректная работа функционала блочного программирования +* Работа функций сохранение/загрузка/удаление +* Работа с pigpiod +* Работа всех примеров + +### Дополнительно + +* ROS ноды не падают в случае потери всех соединений (удобно проверять с экраном) +* Работает `rosshow` +* Работает `espeak` +* *Работает LIRC* +* *Работа iOS-пульта из коробки* +* *Работа Android-пульта из коробки* diff --git a/docs/ru/web_video_server.md b/docs/ru/web_video_server.md index 87d4a319..e01cd687 100644 --- a/docs/ru/web_video_server.md +++ b/docs/ru/web_video_server.md @@ -20,7 +20,7 @@ http://192.168.11.1:8080/stream_viewer?topic=/main_camera/image_raw&type=mjpeg&q ## Просмотр через rqt_image_view -Для просмотра изображений через инструменты rqt необходим компьютер с установленной Ubuntu 18.04 и [ROS Melodic](http://wiki.ros.org/melodic/Installation/Ubuntu). +Для просмотра изображений через инструменты rqt необходим компьютер с установленной Ubuntu 20.04 и [ROS Noetic](http://wiki.ros.org/noetic/Installation/Ubuntu). [Подключитесь к Wi-Fi сети Клевера](wifi.md) и запустите `rqt_image_view` с указанием его IP-адреса: diff --git a/docs/ru/zerotire_vpn.md b/docs/ru/zerotier_vpn.md similarity index 75% rename from docs/ru/zerotire_vpn.md rename to docs/ru/zerotier_vpn.md index 9520a4b0..6057a488 100644 --- a/docs/ru/zerotire_vpn.md +++ b/docs/ru/zerotier_vpn.md @@ -1,38 +1,38 @@ -# Создание виртуальной сети ZeroTire и подключение к ней +# Создание виртуальной сети ZeroTier и подключение к ней ## Создание и настройка сети ZeroTier 1. Зайдите на сайт [ZeroTier](https://www.zerotier.com/). - + 2. Зарегистрируйтесь в ZeroTier. - + 3. Зайдите в свой аккаунт. 4. Нажмите кнопку *Create A Network*. - + 5. После этого вы увидите созданную вами сеть, ее ID и название. Для настройки сети нажмите на нее. - + 6. В открывшемся окне можно изменить имя сети и приватность подключения. - + 7. Пролистайте ниже, до графы *Members*. В ней будет написано о том, что в сети нету пользователей. - + 8. Устройства подключенные к сети будут отображаться в данной графе, для того, чтобы позволить им подключиться к сети, активируйте чекбокс *Auth?*. При этом, подключенному устройству автоматически выдастся внутренний IP адрес, в дальнейшем он будет использоваться для связи с данным устройством.
- - + +
> **Hint** Указывайте имена для новых устройств, этом поможет вам в дальнейшем отличать их друг от друга. @@ -47,17 +47,17 @@ 1. Перейдите на сайт ZeroTier. - + 2. Нажмите на иконку Windows. - + 3. Скачайте и запустите файл `ZeroTier One.msi`.
- - + +
### Подключение к сети @@ -68,15 +68,15 @@ 3. Нажмите на кнопку *Join Network...* для подключения к сети. - + 4. В появившемся окне введите ID вашей сети и нажмите кнопку *Join*. - + 5. Разрешите использование новой сети. - + ## Настройка на iOS @@ -84,11 +84,11 @@ 1. Перейдите на сайт ZeroTier. - + 2. Нажмите на иконку iOS. - + 3. Установите приложение *ZeroTier One*. @@ -98,23 +98,23 @@ 2. Нажмите на *+* для добавления нового подключения. - + 3. Подтвердите политику конфиденциальности. - + 4. Введите ID вашей сети и нажмите кнопку *Add Network*. - + 5. Подтвердите добавление новой конфигурации VPN. 6. Подключитесь к VPN сети, сдвинув ползунок активации сети.
- - + +
## Настройка на Linux (PC, Raspberry Pi) @@ -135,7 +135,7 @@ 2. Введите команду `sudo zerotier-cli join network-id`, где `network-id` это ID вашей сети. - + 3. При успешном подключении, в консоль будет выведено соответствующее сообщение. @@ -145,11 +145,11 @@ 1. Перейдите на сайт ZeroTier. - + 2. Нажмите на иконку macOS. - + 3. Скачайте и запустите файл `ZeroTier One.pkg`. @@ -163,23 +163,23 @@ 3. В открывшемся окне нажмите *Join Network...*. - + 4. В поле *Enter Network ID* введите ID вашей сети. - + ## Подключение к коптеру 1. Убедитесь, что ZeroTier работает и имеет соединение с сетью на дроне и управляющем устройстве. Для этого убедитесь, что интересующие вас устройства имеют статус *Online*. - + 2. Убедитесь, что у всех устройств есть локальные IP адреса - *Managed IPs*. 3. Откройте GQC и во вкладке *Comm Links* добавьте TCP подключение, указав IP дрона. Подробнее про удаленное подключение читайте [тут](gcs_bridge.md).
- - + +
diff --git a/redirects.json b/redirects.json index e8b41013..a9421177 100644 --- a/redirects.json +++ b/redirects.json @@ -68,6 +68,8 @@ { "from": "ru/microsd_images.html", "to": "image.html" }, { "from": "en/microsd_images.html", "to": "image.html" }, { "from": "ru/px4_parameters.html", "to": "parameters.html" }, - { "from": "en/px4_parameters.html", "to": "parameters.html" } + { "from": "en/px4_parameters.html", "to": "parameters.html" }, + { "from": "ru/zerotire_vpn.html", "to": "zerotier_vpn.html" }, + { "from": "en/zerotire_vpn.html", "to": "zerotier_vpn.html" } ] } diff --git a/roswww_static/package.xml b/roswww_static/package.xml index b696c6c7..b98dba6a 100644 --- a/roswww_static/package.xml +++ b/roswww_static/package.xml @@ -1,7 +1,7 @@ roswww_static - 0.21.1 + 0.23.0 Static web pages for ROS packages Oleg Kalachev MIT