diff --git a/aruco_pose/map/map.txt b/aruco_pose/map/map.txt index 3d4dd1ee..970a77df 100644 --- a/aruco_pose/map/map.txt +++ b/aruco_pose/map/map.txt @@ -1,3 +1,4 @@ +# id length x y z rot_z rot_y rot_x 1 0.33 0 0 0 0 0 0 2 0.33 1 0 0 0 0 0 3 0.33 0 1 0 0 0 0 diff --git a/aruco_pose/src/genmap.py b/aruco_pose/src/genmap.py index 72400c52..81fc76b9 100755 --- a/aruco_pose/src/genmap.py +++ b/aruco_pose/src/genmap.py @@ -13,17 +13,21 @@ Generate map file for aruco_map nodelet. Usage: - genmap.py [--top-left] + genmap.py [] [--top-left | --bottom-left] genmap.py (-h | --help) Options: - Marker side length - Marker count along X axis - Marker count along Y axis - Distance between markers along X axis - Distance between markers along Y axis - First marker ID - --top-left First marker is on top-left (not bottom-left) + Marker side length + Marker count along X axis + Marker count along Y axis + Distance between markers along X axis + Distance between markers along Y axis + First marker ID [default: 0] + --top-left First marker is on top-left (default) + --bottom-left First marker is on bottom-left + +Example: + rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > $(catkin_find aruco_pose map)/test_map.txt """ from __future__ import print_function @@ -34,20 +38,21 @@ from docopt import docopt arguments = docopt(__doc__) length = float(arguments['']) -first = int(arguments['']) +first = int(arguments[''] if arguments[''] is not None else 0) markers_x = int(arguments['']) markers_y = int(arguments['']) dist_x = float(arguments['']) dist_y = float(arguments['']) -top_left = arguments['--top-left'] +bottom_left = arguments['--bottom-left'] max_y = (markers_y - 1) * dist_y +print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x') for y in range(markers_y): for x in range(markers_x): pos_x = x * dist_x pos_y = y * dist_y - if top_left: + if not bottom_left: pos_y = max_y - pos_y print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t'.format(first, length, pos_x, pos_y, 0, 0, 0, 0)) first += 1 diff --git a/builder/assets/examples/flight.py b/builder/assets/examples/flight.py new file mode 100644 index 00000000..5234e189 --- /dev/null +++ b/builder/assets/examples/flight.py @@ -0,0 +1,31 @@ +# Information: https://clever.coex.tech/en/programming.html + +import rospy +from clover import srv +from std_srvs.srv import Trigger + +rospy.init_node('flight') + +get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry) +navigate = rospy.ServiceProxy('navigate', srv.Navigate) +navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal) +set_position = rospy.ServiceProxy('set_position', srv.SetPosition) +set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity) +set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude) +set_rates = rospy.ServiceProxy('set_rates', srv.SetRates) +land = rospy.ServiceProxy('land', Trigger) + +# Takeoff and hover 1 m above the ground +navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True) + +# Wait for 3 seconds +rospy.sleep(3) + +# Fly forward 1 m +navigate(x=1, y=0, z=0, frame_id='body') + +# Wait for 3 seconds +rospy.sleep(3) + +# Perform landing +land() diff --git a/builder/assets/examples/leds.py b/builder/assets/examples/leds.py new file mode 100644 index 00000000..9a015482 --- /dev/null +++ b/builder/assets/examples/leds.py @@ -0,0 +1,25 @@ +# Information: https://clever.coex.tech/en/leds.html + +import rospy +from clover.srv import SetLEDEffect + +rospy.init_node('leds') + +set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect) # define proxy to ROS-service + +set_effect(r=255, g=0, b=0) # fill strip with red color +rospy.sleep(2) + +set_effect(r=0, g=100, b=0) # fill strip with green color +rospy.sleep(2) + +set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color +rospy.sleep(2) + +set_effect(effect='flash', r=255, g=0, b=0) # flash twice with red color +rospy.sleep(5) + +set_effect(effect='blink', r=255, g=255, b=255) # blink with white color +rospy.sleep(5) + +set_effect(effect='rainbow') # show rainbow diff --git a/builder/assets/init_rpi.sh b/builder/assets/init_rpi.sh index 606813e6..8054ebfd 100755 --- a/builder/assets/init_rpi.sh +++ b/builder/assets/init_rpi.sh @@ -38,7 +38,12 @@ echo_stamp() { NEW_SSID='clover-'$(head -c 100 /dev/urandom | xxd -ps -c 100 | sed -e "s/[^0-9]//g" | cut -c 1-4) echo_stamp "Setting SSID to ${NEW_SSID}" # TODO: Use wpa_cli insted direct file edit -cat << EOF >> /etc/wpa_supplicant/wpa_supplicant.conf +# FIXME: We rely on raspberrypi-net-mods to copy our file to /etc/wpa_supplicant. +# This is not very reliable, but seems to fix our rfkill problem. +cat << EOF >> /boot/wpa_supplicant.conf +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev +update_config=1 +country=GB network={ ssid="${NEW_SSID}" psk="cloverwifi" @@ -51,9 +56,6 @@ network={ } EOF -echo_stamp "Unblocking wireless interface" -rfkill unblock wifi - NEW_HOSTNAME=$(echo ${NEW_SSID} | tr '[:upper:]' '[:lower:]') echo_stamp "Setting hostname to $NEW_HOSTNAME" hostnamectl set-hostname $NEW_HOSTNAME diff --git a/builder/image-build.sh b/builder/image-build.sh index a3013b8d..4c0a2d70 100755 --- a/builder/image-build.sh +++ b/builder/image-build.sh @@ -104,6 +104,8 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/butterf ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.service' '/lib/systemd/system/' # software install ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh' +# examples +${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # network setup ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh' diff --git a/builder/test/tests.sh b/builder/test/tests.sh index a8dcd43f..f9f28128 100755 --- a/builder/test/tests.sh +++ b/builder/test/tests.sh @@ -52,3 +52,6 @@ rosversion usb_cam rosversion cv_camera rosversion web_video_server rosversion rosshow + +# validate examples are present +[[ $(ls /home/pi/examples/*) ]] diff --git a/clover/CMakeLists.txt b/clover/CMakeLists.txt index a97efc8d..23c48451 100644 --- a/clover/CMakeLists.txt +++ b/clover/CMakeLists.txt @@ -81,6 +81,7 @@ add_service_files( SetAttitude.srv SetRates.srv SetLEDEffect.srv + Execute.srv ) ## Generate actions in the 'action' folder @@ -167,6 +168,8 @@ add_executable(vpe_publisher src/vpe_publisher.cpp) add_executable(led src/led.cpp) +add_executable(shell src/shell.cpp) + target_link_libraries(simple_offboard ${catkin_LIBRARIES} ${GeographicLib_LIBRARIES} @@ -180,10 +183,14 @@ target_link_libraries(vpe_publisher ${catkin_LIBRARIES}) target_link_libraries(led ${catkin_LIBRARIES}) +target_link_libraries(shell ${catkin_LIBRARIES}) + add_dependencies(simple_offboard ${PROJECT_NAME}_generate_messages_cpp) add_dependencies(led ${PROJECT_NAME}_generate_messages_cpp) +add_dependencies(shell ${PROJECT_NAME}_generate_messages_cpp) + ## 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/launch/clover.launch b/clover/launch/clover.launch index 50646e66..61a03084 100644 --- a/clover/launch/clover.launch +++ b/clover/launch/clover.launch @@ -10,6 +10,7 @@ + @@ -76,4 +77,7 @@ + + + diff --git a/clover/src/led.cpp b/clover/src/led.cpp index 3b00c52c..73ca6059 100644 --- a/clover/src/led.cpp +++ b/clover/src/led.cpp @@ -263,7 +263,10 @@ void handleMavrosState(const mavros_msgs::State& msg) // remove the part before "." mode = mode.substr(mode.find(".") + 1); } - notify(mode); + std::string err; + if (ros::names::validate(mode, err)) { + notify(mode); + } } mavros_state = msg; } diff --git a/clover/src/selfcheck.py b/clover/src/selfcheck.py index 3ffa40b3..72de123a 100755 --- a/clover/src/selfcheck.py +++ b/clover/src/selfcheck.py @@ -339,8 +339,11 @@ def is_process_running(binary, exact=False, full=False): @check('ArUco markers') def check_aruco(): if is_process_running('aruco_detect', full=True): - info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length')) - known_tilt = rospy.get_param('aruco_detect/known_tilt') + try: + info('aruco_detect/length = %g m', rospy.get_param('aruco_detect/length')) + except KeyError: + failure('aruco_detect/length parameter is not set') + known_tilt = rospy.get_param('aruco_detect/known_tilt', '') if known_tilt == 'map': known_tilt += ' (ALL markers are on the floor)' elif known_tilt == 'map_flipped': @@ -356,7 +359,7 @@ def check_aruco(): return if is_process_running('aruco_map', full=True): - known_tilt = rospy.get_param('aruco_map/known_tilt') + known_tilt = rospy.get_param('aruco_map/known_tilt', '') if known_tilt == 'map': known_tilt += ' (marker\'s map is on the floor)' elif known_tilt == 'map_flipped': diff --git a/clover/src/shell.cpp b/clover/src/shell.cpp new file mode 100644 index 00000000..06a9bd54 --- /dev/null +++ b/clover/src/shell.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +ros::Duration timeout; + +// TODO: handle timeout +bool handle(clover::Execute::Request& req, clover::Execute::Response& res) +{ + ROS_INFO("Execute: %s", req.cmd.c_str()); + + std::array buffer; + std::string result; + + FILE *fp = popen(req.cmd.c_str(), "r"); + + if (fp == NULL) { + res.code = clover::Execute::Request::CODE_FAIL; + res.output = "popen() failed"; + return true; + } + + while (fgets(buffer.data(), buffer.size(), fp) != nullptr) { + res.output += buffer.data(); + } + + res.code = pclose(fp); + return true; +} + + +int main(int argc, char **argv) +{ + ros::init(argc, argv, "shell"); + ros::NodeHandle nh, nh_priv("~"); + + timeout = ros::Duration(nh_priv.param("timeout", 3.0)); + + auto gt_serv = nh.advertiseService("exec", &handle); + + ROS_INFO("shell: ready"); + ros::spin(); +} diff --git a/clover/src/simple_offboard.cpp b/clover/src/simple_offboard.cpp index 0db67756..af79a1df 100644 --- a/clover/src/simple_offboard.cpp +++ b/clover/src/simple_offboard.cpp @@ -90,7 +90,7 @@ PositionTarget position_raw_msg; AttitudeTarget att_raw_msg; Thrust thrust_msg; TwistStamped rates_msg; -TransformStamped target; +TransformStamped target, setpoint; geometry_msgs::TransformStamped body; // State @@ -433,6 +433,17 @@ void publish(const ros::Time stamp) position_raw_msg.position = position_msg.pose.position; position_raw_pub.publish(position_raw_msg); } + + // publish setpoint frame + if (!setpoint.child_frame_id.empty()) { + setpoint.transform.translation.x = position_msg.pose.position.x; + setpoint.transform.translation.y = position_msg.pose.position.y; + setpoint.transform.translation.z = position_msg.pose.position.z; + setpoint.transform.rotation = position_msg.pose.orientation; + setpoint.header.frame_id = position_msg.header.frame_id; + setpoint.header.stamp = position_msg.header.stamp; + transform_broadcaster->sendTransform(setpoint); + } } if (setpoint_type == VELOCITY) { @@ -764,6 +775,7 @@ int main(int argc, char **argv) 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); nh_priv.param("land_only_in_offboard", land_only_in_offboard, true); nh_priv.param("nav_from_sp", nav_from_sp, true); diff --git a/clover/srv/Execute.srv b/clover/srv/Execute.srv new file mode 100644 index 00000000..b61a9509 --- /dev/null +++ b/clover/srv/Execute.srv @@ -0,0 +1,7 @@ +int32 CODE_FAIL = -1 +int32 CODE_TIMEOUT = -2 + +string cmd +--- +string output +int32 code diff --git a/clover/test/basic.py b/clover/test/basic.py index 885f9f8b..1e4c2d62 100755 --- a/clover/test/basic.py +++ b/clover/test/basic.py @@ -2,6 +2,7 @@ import rospy import pytest from mavros_msgs.msg import State +from clover import srv @pytest.fixture() def node(): @@ -27,3 +28,19 @@ def test_simple_offboard_services_available(): def test_web_video_server(node): import urllib2 urllib2.urlopen("http://localhost:8080").read() + +def test_shell(node): + execute = rospy.ServiceProxy('exec', srv.Execute) + execute.wait_for_service(5) + + res = execute(cmd='echo foo') + assert res.code == 0 + assert res.output == 'foo\n' + + res = execute(cmd='foo') + assert res.code == 32512 + assert res.output == '' + + res = execute(cmd='ls foo') + assert res.code == 512 + assert res.output == '' diff --git a/clover/test/basic.test b/clover/test/basic.test index aa297005..46a923e6 100755 --- a/clover/test/basic.test +++ b/clover/test/basic.test @@ -32,6 +32,8 @@ + + startup: { r: 255, g: 255, b: 255 } diff --git a/clover/www/index.html b/clover/www/index.html index d25ea74d..3da27946 100644 --- a/clover/www/index.html +++ b/clover/www/index.html @@ -8,7 +8,17 @@
  • 3D visualization for markers map (ros3djs)
  • +
    + + diff --git a/docs/assets/camera_calibration.png b/docs/assets/camera_calibration.png new file mode 100644 index 00000000..5f3d18f5 Binary files /dev/null and b/docs/assets/camera_calibration.png differ diff --git a/docs/assets/chessboard.pdf b/docs/assets/chessboard.pdf new file mode 100644 index 00000000..0d24c45f Binary files /dev/null and b/docs/assets/chessboard.pdf differ diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index d401f316..3eec1121 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -13,6 +13,7 @@ * [RC setup](radio.md) * [Flight modes](modes.md) * [Power setup](power.md) + * [Failsafe configuration](failsafe.md) * Working with Raspberry Pi * [Raspberry Pi](raspberry.md) * [RPi Image](image.md) diff --git a/docs/en/aruco_map.md b/docs/en/aruco_map.md index a17d758d..547b6a78 100644 --- a/docs/en/aruco_map.md +++ b/docs/en/aruco_map.md @@ -53,7 +53,7 @@ Grid maps may be generated using the `genmap.py` script: rosrun aruco_pose genmap.py length x y dist_x dist_y first > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt ``` -`length` is the size of each marker, `x` is the marker count along the *x* axis, `y` is the marker count along the *y* axis, `dist_x` is the distance between the centers of adjacent markers along the *x* axis, `dist_y` is the distance between the centers of the *y* axis, `first` is the ID of the first marker (bottom left marker, unless `--top-left` is specified), `test_map.txt` is the name of the generated map file. The optional `--top-left` parameter changes the numbering of markers, making the top left marker the first one. +`length` is the size of each marker, `x` is the marker count along the *x* axis, `y` is the marker count along the *y* axis, `dist_x` is the distance between the centers of adjacent markers along the *x* axis, `dist_y` is the distance between the centers of the *y* axis, `first` is the ID of the first marker (top left marker, unless `--bottom-left` is specified), `test_map.txt` is the name of the generated map file. The optional `--bottom-left` parameter changes the numbering of markers, making the bottom left marker the first one. Usage example: @@ -61,6 +61,8 @@ Usage example: rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt ``` +Additional information on the utility can be obtained using `-h` key: `rosrun aruco_pose genmap.py -h`. + ### Checking the map diff --git a/docs/en/contributing.md b/docs/en/contributing.md index d88c0766..a969bb71 100644 --- a/docs/en/contributing.md +++ b/docs/en/contributing.md @@ -6,19 +6,21 @@ Clever is mostly an [open source](https://en.wikipedia.org/wiki/Open-source_soft ## Markdown -All Clever documentation is written in the widespread [Markdown](https://ru.wikipedia.org/wiki/Markdown) format. There are many guides on it on the Internet. +All Clever documentation is written in the widespread [Markdown](https://en.wikipedia.org/wiki/Markdown) format. There are many Markdown guides on the Internet. In Russian: https://guides.hexlet.io/markdown/. In English: https://www.markdownguide.org/getting-started, https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet. -For the ease of editing texts, you may use text editors with markdown support [Typora](https://typora.io), [Dillinger](https://dillinger.io/) (web), [VSCode](https://code.visualstudio.com) with the [Markdown Editor plugin](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor). +For the ease of editing texts, you may use text editors with Markdown support: [Typora](https://typora.io), [Dillinger](https://dillinger.io/) (web), [VSCode](https://code.visualstudio.com) with the [Markdown Editor plugin](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor). + +We also recommend using the [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) VScode plugin. For a local build of a static documentation website, use the [`gitbook-cli`](https://github.com/GitbookIO/gitbook-cli) utility. -## Correcting errors in the documents +## Fixing documentation errors -If you have found an error in the documents, or if you want to improve it, use the Pull Request mechanism. +If you have found an error in the documentation or if you want to improve it, use the **Pull Request** mechanism. 1. Find a file with the article you want in the repository – https://github.com/CopterExpress/clever/tree/master/docs. 2. Click "Edit". @@ -32,14 +34,61 @@ If you have found an error in the documents, or if you want to improve it, use t More information about Pull Requests is available [at GitHub](https://help.github.com/articles/about-pull-requests/) (English) or in [GIT documentation](https://git-scm.com/book/ru/v2/GitHub-contributing-to_projects) (Russian). - +> **Note** If you've made your own project based on Clever, you can add an article about it to the "Clever-based projects" section. -## Your project with Clever +Prepare your article and send it as a pull request to the [Clever repository](https://github.com/CopterExpress/clever). -If you have implemented your own interesting project using clever, you can add an article about it in section "Clever-based projects". +1. Fork the Clever repository: - + GitHub Fork + +2. Check out the freshly-forked repository on your computer: + + ```bash + git clone https://github.com//clever.git + ``` + +3. Open the directory with the source code checkout and create a new branch for your article (for example, `new-article`): + + ```bash + git checkout -b new-article + ``` + +4. Write a new article in the [Markdown](https://en.wikipedia.org/wiki/Markdown) format and save it in the `docs/ru` or `docs/en` folder (for example, `docs/en/new_article.md`). +5. Place additional visual assets in the `docs/assets` folder and add them to your article. +6. Add a link to your article to the appropriate section in the `SUMMARY.md` file (in the same folder as in the fourth step): + + ```markdown + ... + * Supplementary materials + * [COEX Pix](coex_pix.md) + * [Contribution guidelines](contributing.md) + * [New article](new_article.md) + * [RC troubleshooting](radioerrors.md) + * [Flashing ESCs](esc_firmware.md) + ... + ``` + +7. Commit your changes locally: + + ```bash + git add docs/ + git commit -m "Add new article for Clever" + ``` + +8. Upload your branch to your forked repository on GitHub: + + ```bash + git push -u origin new-article + ``` + +9. Open your repository on GitHub and send a `pull request` from your branch to Clever: + + GitHub Pull Request + + GitHub Create Pull + +10. Wait for the review, be ready to make changes if needed. +11. Look at your new and useful article at https://clever.coex.tech ! diff --git a/docs/en/failsafe.md b/docs/en/failsafe.md new file mode 100644 index 00000000..d77217e4 --- /dev/null +++ b/docs/en/failsafe.md @@ -0,0 +1,13 @@ +# Failsafe configuration + +Main article is available at https://docs.px4.io/master/en/config/safety.html. + +The *Safety* panel allows you to configure actions that should be performed when a failsafe is triggered. You should at the very least configure the RC Loss failsafe, which is triggered when the RC transmitter link is lost: + +1. Open the *Safety* panel. +2. Select one of the following actions in the *RC Loss Failsafe Trigger* option: + * *Land mode* – transition to automatic land mode; + * *Terminate* – set all outputs to their failsafe values. +3. Set the timeout value before RC Loss triggers in the *RC Loss Timeout* field. We recommend setting it to 0.5 s. + +QGroundControl failsafe diff --git a/docs/en/frames.md b/docs/en/frames.md index b44bddb3..f1afb949 100644 --- a/docs/en/frames.md +++ b/docs/en/frames.md @@ -10,7 +10,8 @@ Main frames in the `clever` package: * `map` has its origin at the flight controller initialization point and may be considered stationary. It is shown as a white grid on the image above; * `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). +* `navigate_target` is bound to the current navigation target (as set by the [navigate](simple_offboard.md#navigate) service); +* `setpoint` is current position setpoint. Additional frames become available when [ArUco positioning system](aruco.md) is active: diff --git a/docs/en/power.md b/docs/en/power.md index e4695b1f..5e6d96bd 100644 --- a/docs/en/power.md +++ b/docs/en/power.md @@ -31,3 +31,5 @@ Further reading: https://docs.qgroundcontrol.com/en/SetupView/Power.html. Further reading: https://docs.px4.io/v1.9.0/en/advanced_config/esc_calibration.html. + +**Next**: [Failsafe configuration](failsafe.md) diff --git a/docs/en/simple_offboard.md b/docs/en/simple_offboard.md index 5441e2e6..167d8de9 100644 --- a/docs/en/simple_offboard.md +++ b/docs/en/simple_offboard.md @@ -146,7 +146,7 @@ Flying 2 m to the left from the last navigation target: navigate(x=0, y=2, z=0, speed=1, frame_id='navigate_target') ``` -Turn 90 degrees counterclockwise: +Turn 90 degrees clockwise: ```python navigate(yaw=math.radians(-90), frame_id='body') diff --git a/docs/ru/SUMMARY.md b/docs/ru/SUMMARY.md index 2b5d2753..35d9c6b3 100644 --- a/docs/ru/SUMMARY.md +++ b/docs/ru/SUMMARY.md @@ -53,6 +53,7 @@ * [CAD-модели Клевера](models.md) * [Docker-контейнер с симулятором](sitl_docker.md) * [Установка ROS Melodic](ros-install.md) + * [Калибровка камеры](camera_calibration.md) * [Управление мультикоптером при помощи 4G связи](4g.md) * [Пакеты Клевера на Jetson Nano](jetson_nano.md) * [Пилотирование со смартфона](rc.md) @@ -66,7 +67,6 @@ * [Неисправности радиоаппаратуры](radioerrors.md) * [Прошивка ESC контроллеров](esc_firmware.md) * [Настройка режима тренера](trainer_mode.md) - * [Калибровка камеры](camera_calibration.md) * [Взаимодействие с Arduino](arduino.md) * [Подключение GPS](gps.md) * [Работа с ИК датчиками](ir_sensors.md) @@ -95,6 +95,7 @@ * [Подсчет количества объектов c камеры](object_counting.md) * [Пульт на Андроид](android.md) * [Блочный конструктор полета](clever_blocks.md) + * [Калибровка камеры (legacy)](camera_calib.md) * [Управление дроном для оценки позы человека](human_pose_estimation_drone_control.md) ## Учебник diff --git a/docs/ru/aruco_map.md b/docs/ru/aruco_map.md index 94daffc1..6966ebfd 100644 --- a/docs/ru/aruco_map.md +++ b/docs/ru/aruco_map.md @@ -53,7 +53,7 @@ id_маркера размер_маркера x y z угол_z угол_y уго rosrun aruco_pose genmap.py length x y dist_x dist_y first > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt ``` -Где `length` – размер маркера, `x` – количество маркеров по оси *x*, `y` - количество маркеров по оси *y*, `dist_x` – расстояние между центрами маркеров по оси *x*, `y` – расстояние между центрами маркеров по оси *y*, `first` – ID первого (левого нижнего) маркера, `test_map.txt` – название файла с картой. Дополнительный ключ `--top-left` позволяет нумеровать маркеры с левого верхнего угла. +Где `length` – размер маркера, `x` – количество маркеров по оси *x*, `y` - количество маркеров по оси *y*, `dist_x` – расстояние между центрами маркеров по оси *x*, `y` – расстояние между центрами маркеров по оси *y*, `first` – ID первого (левого нижнего) маркера, `test_map.txt` – название файла с картой. Дополнительный ключ `--bottom-left` позволяет нумеровать маркеры с левого нижнего угла. Пример: @@ -61,7 +61,9 @@ rosrun aruco_pose genmap.py length x y dist_x dist_y first > ~/catkin_ws/src/cle rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt ``` -Также, можно создать карту в специальном [конструкторе](arucogenmap.md). +Дополнительную информацию по утилите можно получить по ключу `-h`: `rosrun aruco_pose genmap.py -h`. + +Также можно создать карту в специальном [конструкторе](arucogenmap.md). ### Проверка diff --git a/docs/ru/camera_calib.md b/docs/ru/camera_calib.md new file mode 100644 index 00000000..db5b3a87 --- /dev/null +++ b/docs/ru/camera_calib.md @@ -0,0 +1,245 @@ +# Калибровка камеры + +Для точной работы систем компьютерного зрения (например, для навигации по ArUco-маркерам) используемая камера должна быть откалибрована. + +![distorted](../assets/img1.jpg) + +> Изображение "скруглено" ближе к краям. +Какой-либо алгоритм компьютерного зрения будет воспринимать информацию с этой картинки неправильно. + +## Установка приложения + +Для начала, необходимо установить необходимые библиотеки: + +```bash +pip install numpy +pip install opencv-python +pip install pyyaml +pip install urllib2 +pip install flask, flask-wtf +``` + +Затем скачиваем исходный код из репозитория и проводим установку: + +```bash +git clone https://github.com/tinderad/calibration_web_2.7.git +cd calibration_web_2.7.git +sudo python setup.py build +sudo python setup.py install +``` + +## Подготовка к калибровке + +Вам необходимо подготовить калибровочную мишень. Она представляет собой «шахматную доску». Файл можно взять [отсюда](https://www.oreilly.com/library/view/learning-opencv-3/9781491937983/assets/lcv3_ac01.png). +Наклейте распечатанную мишень на любую твердую поверхность. Посчитайте количество пересечений в длину и в ширину доски, измерьте размер клетки (в мм), как указано на изображении. + +![asd](../assets/chessboard.jpg) + +Включите Клевер и подключитесь к его Wifi. + +> Перейдите на _192.168.11.1:8080_ и проверьте, получает ли компьютер изображения из топика _image_raw_. + +## Калибровка + +Подключитесь к Клеверу по протоколу SSH (например, при помощи PuTTY). + +Запустите приложение: + +```bash +>cd calibration_web_2.7/ccc_server +>python app.py +``` + +Далее вам необходимо на компьютере открыть в браузере страницу по адресу _192.168.11.1:8081_ + +> Порт можно настроить в файле _ccc_server/config.py_. + +На открытой странице необходимо ввести параметры калибровочной мишени: количество перекрестий в длину и ширину, длину ребра квадрата. Для начала калибровки нажмите кнопку **_Start Calibration_**. + +![asd](../assets/cam_calib1.png) + +На следующей странице при помощи кнопки **_Catch photo_** можно делать фотографии калибровочной мишени. + +![asd](../assets/cam_calib2.png) + +Если программа нашла на изображении указанную мишень, откроется страница, на которой вам необходимо подтвердить корректность найденных перекрестий. + +![asd](../assets/cam_calib3.png) + +Если перекрестия были распознаныы правильно, нажмите на клавишу **_Add_**, и перейдите к получению новых фотографий. В противном же случае, ели перекристия были распознаны некорректно, пропустите данную фотографию при помощи клавиши **_Skip_**. + +>В большинстве случаев найденные углы будут подсвечиваться разными цветами, но иногда подсветка будет становиться красной. это происходит в том случае, если углы распознаны, но неточно. + +Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов. После преодоления данного порога появится кнопка **_Finish_**, по нажатию на которую начнется генерация калибровочного файла. + +>Это может занять некоторое время. + +На открывшейся странице выведется информация о результате калибровки: имя файла и re-projection error. +>re-projection error - отклонение от стандартной математической модели. Чем эта величина меньше, тем точнее проведена калибровка. + +![asd](../assets/cam_calib4.png) + +Программа обработает все полученные фотографии, и создаст **_.yaml_** файл в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры. + +> Если вы поменяете разрешение получаемого изображения, вам нужно будет снова калибровать камеру. + +## Предыдущая версия + +Также вы можете воспользоваться предыдущей версией программы, не имеющей web-интерфейса. + +Запустите скрипт **_calibrate_cam_**: + +```bash +>calibrate_cam +``` + +Задайте параметры мишени: + +```bash +>calibrate_cam +Chessboard width: # Перекрестий в ширину +Chessboard height: # Перекрестий в длину +Square size: # Длина ребра клетки (в мм) +Saving mode (YES - on): # Режим сохранения +``` + +> Режим сохранения: если включен, то все полученные фотографии будут сохраняться в нынешней директории. + +Скрипт начнет свою работу: + +``` +Calibration started! +Commands: +help, catch (key: Enter), delete, restart, stop, finish +``` + +Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов. + +Чтобы сделать фото, введите команду **_catch_**. + +```bash +>catch +``` + +Программа будет информировать вас о состоянии калибровки. + +```bash +... +Chessboard not found, now 0 (25 required) +> # Enter +--- +Image added, now 1 (25 required) +``` + +> Вместо того, чтобы каждый раз вводить команду **_catch_**, Вы можете просто нажимать клавишу **_Enter_** (вводить пустую строку). + +После того, как будет набрано достаточное количество изображений, введите команду **_finish_**. + +```bash +... +>finish +Calibration successful! +``` + +**Калибровка по существующим изображениям:** + +Если же у вас уже есть изображения, то вы можете откалибровать камеру по ним при помощи скрипта **_calibrate_from_dir_**. + +```bash +>calibrate_from_dir +``` + +Указываем характеристики мишени, а так же путь до папки с изображениями: + +```bash +>calibrate_cam_ex +Chessboard width: # Перекрестий в ширину +Chessboard height: # Перекрестий в длину +Square size: # Длина ребра клетки (в мм) +Path: # Путь до папки с изображениями +``` + +В остальном этот скрипт работает аналогично **_calibrate_cam_**. + +Программа обработает все полученные фотографии, и создаст файл **_camera_info_****_._****_yaml_** в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры. + +## Исправление искажений + +За получение исправленного изображения отвечает функция +**clever_cam_calibration._get_undistorted_image(cv2_image, camera_info)_**: + +* **_cv2_image_**: Закодированное в массив cv2 изображение. +* **_camera_****_­__****_info_**: Путь до файла калибровки. + +Функция возвращает массив cv2, в котором закодировано исправленное изображение. + +> Если вы используете fisheye-камеру, поставляемую вместе с Клевером, то для обработки изображений разрешением 320x240 или 640x480 вы можете использовать уже существующие параметры калибровки. Для этого в качестве аргумента **_camera_info_** передайте параметры **_clever_cam_calibration.CLEVER_FISHEYE_CAM_320_** или **_clever_cam_calibration.CLEVER_FISHEYE_CAM_640_** соответственно. + +## Примеры работы + +Изначальные изображения: + +![asd](../assets/img1.jpg) + +![asd](../assets/img2.jpg) + +Иcправленные изображения: + +![asd](../assets/calibresult.jpg) + +![asd](../assets/calibresult1.jpg) + +## Пример использования + +**Обработка потока изображений с камеры**. + +Данная программа получает изображения с камеры Клевера и выводит их на экран в исправленном виде, используя существующий калибровочный файл. + +```python +import clever_cam_calibration as ccc +import cv2 +import urllib.request +import numpy as np +while True: + req = urllib.request.urlopen('http://192.168.11.1:8080/snapshot?topic=/main_camera/image_raw') + arr = np.asarray(bytearray(req.read()), dtype=np.uint8) + image = cv2.imdecode(arr, -1) + undistorted_img = ccc.get_undistorted_image(image, ccc.CLEVER_FISHEYE_CAM_640) + cv2.imshow("undistort", undistorted_img) + cv2.waitKey(33) +cv2.destroyAllWindows() +``` + +## Использование для ArUco + +Чтобы применить параметры калибровки к системе ArUco-навигации, требуется перенести калибровочный .yaml файл на Raspberry Pi Клевера и инициализировать его. + +> Не забудьте подключиться к WiFI Клевера. + +Для передачи файла используется протокол SFTP. В данном примере используется программа WinSCP. + +Подключимся к Raspberry Pi по SFTP: + +> Пароль: _**raspberry**_ + +![img](../assets/wcp1.png) + +Нажимаем “Войти”. Переходим в _**/home/pi/catkin_ws/src/clever/clever/camera_info/**_ и копируем туда калибровочный .yaml файл: + +![img](../assets/wcp2.jpg) + +Теперь мы должны выбрать этот файл в конфигурации ArUco. Для этого используется связь по протоколу SSH. В данном примере используется программа PuTTY. + +Подключимся к Raspberry Pi по SSH: + +![img](../assets/pty1.jpg) + +Войдем под логином _**pi**_ и паролем _**raspberry**_, перейдем в директорию _**/home/pi/catkin_ws/src/clever/clever/launch**_ и начнем редактировать конфигурацию _**main_camera.launch**_: + +![img](../assets/pty2.jpg) + +В строке _**camera node**_ заменим параметр _**camera_info**_ на _**camera_info.yaml**_: + +![img](../assets/pty3.jpg) + +> Не забудьте изменить разрешение камеры в *main_camera.launch*. diff --git a/docs/ru/camera_calibration.md b/docs/ru/camera_calibration.md index db5b3a87..d836457b 100644 --- a/docs/ru/camera_calibration.md +++ b/docs/ru/camera_calibration.md @@ -1,245 +1,52 @@ # Калибровка камеры -Для точной работы систем компьютерного зрения (например, для навигации по ArUco-маркерам) используемая камера должна быть откалибрована. +Калибровка камеры может значительно повысить качество работы модулей, связанным с компьютерным зрением: [распознавание ArUco-маркеров](aruco.md) и [Optical Flow](optical_flow.md). -![distorted](../assets/img1.jpg) +При калибровке камеры подбираются параметры, наиболее хорошо описывающие конкретный установленный объектив. Данные параметры включают в себя фокусные расстояния, расположение точки principal point (которое зависит от того, насколько ровно по центру установлен объектив), коэффициенты дисторсии *D*. Подробнее про использующуюся модель искажений камеры можно прочитать в [документации OpenCV](https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html). -> Изображение "скруглено" ближе к краям. -Какой-либо алгоритм компьютерного зрения будет воспринимать информацию с этой картинки неправильно. +Существует несколько инструментов, которые позволяют откалибровать камеру и прописать вычисленные параметры в систему. Обычно, они используют калибровочные изображения: "шахматные доски" (*Chessboard*), а так же комбинации шахматной доски и сетки ArUco-маркеров ([*ChArUco*](https://docs.opencv.org/3.4/df/d4a/tutorial_charuco_detection.html)). -## Установка приложения +## ROS-пакет camera_calibration -Для начала, необходимо установить необходимые библиотеки: +Основной туториал: http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration. -```bash -pip install numpy -pip install opencv-python -pip install pyyaml -pip install urllib2 -pip install flask, flask-wtf -``` +Для калибровки камеры с использованием ROS-пакета camera_calibration необходим компьютер с установленным ОС GNU/Linux и [ROS Melodic](ros-install.md). -Затем скачиваем исходный код из репозитория и проводим установку: +ROS Camera Calibrator -```bash -git clone https://github.com/tinderad/calibration_web_2.7.git -cd calibration_web_2.7.git -sudo python setup.py build -sudo python setup.py install -``` +1. Используя Терминал, установите на компьютер пакет `camera_calibration`: -## Подготовка к калибровке + ```bash + sudo apt-get install ros-melodic-camera-calibration + ``` -Вам необходимо подготовить калибровочную мишень. Она представляет собой «шахматную доску». Файл можно взять [отсюда](https://www.oreilly.com/library/view/learning-opencv-3/9781491937983/assets/lcv3_ac01.png). -Наклейте распечатанную мишень на любую твердую поверхность. Посчитайте количество пересечений в длину и в ширину доски, измерьте размер клетки (в мм), как указано на изображении. +2. Скачайте калибровочную доску – [`chessboard.pdf`](../assets/chessboard.pdf). Распечатайте доску на принтере либо выведите ее на экран компьютера. -![asd](../assets/chessboard.jpg) +3. Подключитесь к [Wi-Fi Клевера](wifi.md). -Включите Клевер и подключитесь к его Wifi. +4. Запустите калибровку (на компьютере): -> Перейдите на _192.168.11.1:8080_ и проверьте, получает ли компьютер изображения из топика _image_raw_. + ```bash + ROS_MASTER_URI=http://192.168.11.1:11311 rosrun camera_calibration cameracalibrator.py --size 6x8 --square 0.108 image:=/main_camera/image_raw camera:=/main_camera + ``` -## Калибровка + > **Note** Вместо значения *0.108* укажите реальный размер квадрата на распечатанной доске или на экране (в метрах). Например, значение *0.03* будет соответствовать 3 см. -Подключитесь к Клеверу по протоколу SSH (например, при помощи PuTTY). +5. Когда программа для калибровки запустится, начните перемещать дрон таким образом, чтобы калибровочная доска попадала в кадр под разными углами. -Запустите приложение: + * Перемещайте калибровочную доску в левый, правый, верхний и нижний торец кадра. + * Вращайте калибровочную доску вокруг всех 3-х осей. + * Отдаляйте и приближайте камеру к калибровочной доске. -```bash ->cd calibration_web_2.7/ccc_server ->python app.py -``` +6. Нажмите кнопку *CALIBRATE*, когда она станет активной. Процесс вычисления параметров калибровки займет несколько минут. -Далее вам необходимо на компьютере открыть в браузере страницу по адресу _192.168.11.1:8081_ + Когда калибровка завершится, в терминале вы увидите полученные параметры. В окне отобразится изображение с выправленными искажениями. При успешной калибровке все реальные прямые линии должны остаться прямыми на полученном изображении. -> Порт можно настроить в файле _ccc_server/config.py_. +7. Нажмите *COMMIT*, чтобы сохранить полученные параметры калибровки. Результат будет записан в файл калибровки основной камеры Клевера: +`/home/pi/catkin_ws/src/clever/clever/camera_info/fisheye_cam_320.yaml`. -На открытой странице необходимо ввести параметры калибровочной мишени: количество перекрестий в длину и ширину, длину ребра квадрата. Для начала калибровки нажмите кнопку **_Start Calibration_**. +8. Перезапустите сервисы Клевера: -![asd](../assets/cam_calib1.png) - -На следующей странице при помощи кнопки **_Catch photo_** можно делать фотографии калибровочной мишени. - -![asd](../assets/cam_calib2.png) - -Если программа нашла на изображении указанную мишень, откроется страница, на которой вам необходимо подтвердить корректность найденных перекрестий. - -![asd](../assets/cam_calib3.png) - -Если перекрестия были распознаныы правильно, нажмите на клавишу **_Add_**, и перейдите к получению новых фотографий. В противном же случае, ели перекристия были распознаны некорректно, пропустите данную фотографию при помощи клавиши **_Skip_**. - ->В большинстве случаев найденные углы будут подсвечиваться разными цветами, но иногда подсветка будет становиться красной. это происходит в том случае, если углы распознаны, но неточно. - -Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов. После преодоления данного порога появится кнопка **_Finish_**, по нажатию на которую начнется генерация калибровочного файла. - ->Это может занять некоторое время. - -На открывшейся странице выведется информация о результате калибровки: имя файла и re-projection error. ->re-projection error - отклонение от стандартной математической модели. Чем эта величина меньше, тем точнее проведена калибровка. - -![asd](../assets/cam_calib4.png) - -Программа обработает все полученные фотографии, и создаст **_.yaml_** файл в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры. - -> Если вы поменяете разрешение получаемого изображения, вам нужно будет снова калибровать камеру. - -## Предыдущая версия - -Также вы можете воспользоваться предыдущей версией программы, не имеющей web-интерфейса. - -Запустите скрипт **_calibrate_cam_**: - -```bash ->calibrate_cam -``` - -Задайте параметры мишени: - -```bash ->calibrate_cam -Chessboard width: # Перекрестий в ширину -Chessboard height: # Перекрестий в длину -Square size: # Длина ребра клетки (в мм) -Saving mode (YES - on): # Режим сохранения -``` - -> Режим сохранения: если включен, то все полученные фотографии будут сохраняться в нынешней директории. - -Скрипт начнет свою работу: - -``` -Calibration started! -Commands: -help, catch (key: Enter), delete, restart, stop, finish -``` - -Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов. - -Чтобы сделать фото, введите команду **_catch_**. - -```bash ->catch -``` - -Программа будет информировать вас о состоянии калибровки. - -```bash -... -Chessboard not found, now 0 (25 required) -> # Enter ---- -Image added, now 1 (25 required) -``` - -> Вместо того, чтобы каждый раз вводить команду **_catch_**, Вы можете просто нажимать клавишу **_Enter_** (вводить пустую строку). - -После того, как будет набрано достаточное количество изображений, введите команду **_finish_**. - -```bash -... ->finish -Calibration successful! -``` - -**Калибровка по существующим изображениям:** - -Если же у вас уже есть изображения, то вы можете откалибровать камеру по ним при помощи скрипта **_calibrate_from_dir_**. - -```bash ->calibrate_from_dir -``` - -Указываем характеристики мишени, а так же путь до папки с изображениями: - -```bash ->calibrate_cam_ex -Chessboard width: # Перекрестий в ширину -Chessboard height: # Перекрестий в длину -Square size: # Длина ребра клетки (в мм) -Path: # Путь до папки с изображениями -``` - -В остальном этот скрипт работает аналогично **_calibrate_cam_**. - -Программа обработает все полученные фотографии, и создаст файл **_camera_info_****_._****_yaml_** в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры. - -## Исправление искажений - -За получение исправленного изображения отвечает функция -**clever_cam_calibration._get_undistorted_image(cv2_image, camera_info)_**: - -* **_cv2_image_**: Закодированное в массив cv2 изображение. -* **_camera_****_­__****_info_**: Путь до файла калибровки. - -Функция возвращает массив cv2, в котором закодировано исправленное изображение. - -> Если вы используете fisheye-камеру, поставляемую вместе с Клевером, то для обработки изображений разрешением 320x240 или 640x480 вы можете использовать уже существующие параметры калибровки. Для этого в качестве аргумента **_camera_info_** передайте параметры **_clever_cam_calibration.CLEVER_FISHEYE_CAM_320_** или **_clever_cam_calibration.CLEVER_FISHEYE_CAM_640_** соответственно. - -## Примеры работы - -Изначальные изображения: - -![asd](../assets/img1.jpg) - -![asd](../assets/img2.jpg) - -Иcправленные изображения: - -![asd](../assets/calibresult.jpg) - -![asd](../assets/calibresult1.jpg) - -## Пример использования - -**Обработка потока изображений с камеры**. - -Данная программа получает изображения с камеры Клевера и выводит их на экран в исправленном виде, используя существующий калибровочный файл. - -```python -import clever_cam_calibration as ccc -import cv2 -import urllib.request -import numpy as np -while True: - req = urllib.request.urlopen('http://192.168.11.1:8080/snapshot?topic=/main_camera/image_raw') - arr = np.asarray(bytearray(req.read()), dtype=np.uint8) - image = cv2.imdecode(arr, -1) - undistorted_img = ccc.get_undistorted_image(image, ccc.CLEVER_FISHEYE_CAM_640) - cv2.imshow("undistort", undistorted_img) - cv2.waitKey(33) -cv2.destroyAllWindows() -``` - -## Использование для ArUco - -Чтобы применить параметры калибровки к системе ArUco-навигации, требуется перенести калибровочный .yaml файл на Raspberry Pi Клевера и инициализировать его. - -> Не забудьте подключиться к WiFI Клевера. - -Для передачи файла используется протокол SFTP. В данном примере используется программа WinSCP. - -Подключимся к Raspberry Pi по SFTP: - -> Пароль: _**raspberry**_ - -![img](../assets/wcp1.png) - -Нажимаем “Войти”. Переходим в _**/home/pi/catkin_ws/src/clever/clever/camera_info/**_ и копируем туда калибровочный .yaml файл: - -![img](../assets/wcp2.jpg) - -Теперь мы должны выбрать этот файл в конфигурации ArUco. Для этого используется связь по протоколу SSH. В данном примере используется программа PuTTY. - -Подключимся к Raspberry Pi по SSH: - -![img](../assets/pty1.jpg) - -Войдем под логином _**pi**_ и паролем _**raspberry**_, перейдем в директорию _**/home/pi/catkin_ws/src/clever/clever/launch**_ и начнем редактировать конфигурацию _**main_camera.launch**_: - -![img](../assets/pty2.jpg) - -В строке _**camera node**_ заменим параметр _**camera_info**_ на _**camera_info.yaml**_: - -![img](../assets/pty3.jpg) - -> Не забудьте изменить разрешение камеры в *main_camera.launch*. + ```bash + sudo systemctl restart clever + ``` diff --git a/docs/ru/camera_setup.md b/docs/ru/camera_setup.md index 022afb7a..92f7dab3 100644 --- a/docs/ru/camera_setup.md +++ b/docs/ru/camera_setup.md @@ -80,3 +80,7 @@ > **Hint** [Утилита `selfcheck.py`](selfcheck.md) выдает словесное описание установленной в данной момент ориентации основной камеры. + +## Калибровка + +Для улучшение качества работы алгоритмов также рекомендуется произвести калибровку камеры, процесс которой описан [в отдельной статье](camera_calibration.md). diff --git a/docs/ru/frames.md b/docs/ru/frames.md index dd8a2c4b..0e7519dd 100644 --- a/docs/ru/frames.md +++ b/docs/ru/frames.md @@ -10,7 +10,8 @@ * `map` — координаты относительно точки инициализации полетного контроллера: белая сетка на иллюстрации; * `base_link` — координаты относительно квадрокоптера: схематичное изображение квадрокоптера на иллюстрации; * `body` — координаты относительно квадрокоптера без учета наклонов по тангажу и крену: красная, синяя и зеленая линии на иллюстрации; -* `navigate_target` – координаты точки, в которую сейчас летит дрон (с использованием [navigate](simple_offboard.md#navigate)). +* `navigate_target` – координаты точки, в которую сейчас летит дрон (с использованием [navigate](simple_offboard.md#navigate)); +* `setpoint` – текущий setpoint по позиции. При использовании [системы позиционирования по ArUco-маркерам](aruco.md) появляются дополнительные фреймы: diff --git a/docs/ru/simple_offboard.md b/docs/ru/simple_offboard.md index b86d4ade..2bf9bb0c 100644 --- a/docs/ru/simple_offboard.md +++ b/docs/ru/simple_offboard.md @@ -146,7 +146,7 @@ navigate(x=0, y=-3, z=0, speed=1, frame_id='body') navigate(x=0, y=2, z=0, speed=1, frame_id='navigate_target') ``` -Повернуться на 90 градусов против часовой: +Повернуться на 90 градусов по часовой: ```python navigate(yaw=math.radians(-90), frame_id='body')