Codync
免费开源的 1:1 Grok Bot 与 Muse 替代方案。可在 iPhone、Mac 或 Linux 桌面上将 Claude 代码、Codex、Cursor 以及 40 多个编码代理作为持久机器人使用。支持群组聊天、线程、应用。
AI 翻译
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Implementation of Wickramanayake, Rajasooriya, Ranawella & Karunarathne, "Landing a Quadcopter on to a moving landing target using Computer Vision" (Univ. of Peradeniya, 2019), revisited with a headless closed-loop simulator. The rebuild refines the paper's estimation for closed-loop flight (tilt compensation and a Kalman filter) and measures what each step adds, with tests and experiments in this repo.
Write-up with an interactive replay of the recorded runs: billacode.com/blogs/landing-a-quadcopter-on-a-moving-target-revisiting-my-2019-paper
A quadcopter can take off and land vertically and reach places a piloted aircraft can't, but landing on a moving platform is hard. The drone has to find the target, estimate its relative velocity and heading, cope with acceleration and wind, and still touch down precisely. Earlier work often relies on the drone and the vehicle communicating (sharing GPS or odometry), and in the real world that link cannot be guaranteed. The paper therefore uses only an onboard camera, low-cost hardware (Raspberry Pi + Arduino Nano) and an ArUco landing pad with three marker sizes, so that some marker stays fully visible from the highest altitude down to touchdown. The pipeline is:
estimatePoseSingleMarkers → rvec, tvec)tvec)The 2019 work built detection and speed measurement on real hardware, with a smooth landing left as future work. Flying the drone in a closed loop needs every quantity to be exact and to hold up while the drone tilts, so this rebuild implements Section III-C as follows.
| Quantity | In the paper (Sec. III-C) | In this rebuild | Why |
|---|---|---|---|
| Rotation | rvecs | R = cv2.Rodrigues(rvec) | solvePnP returns rvec directly; as a matrix it drives tilt compensation and pad heading |
| Distance moved | √((tx₁²+ty₁²) − (tx₂²+ty₂²)) | ‖p₂ − p₁‖ on world-frame positions | Plain straight-line distance between two positions |
| Speed | d / duration | Constant-velocity Kalman filter on the pad's world position; relative velocity v_pad − v_drone | Smooths frame-to-frame noise at 30 Hz |
| Direction | angle = tyᵢ / txᵢ | atan2(v_y, v_x) of the filtered velocity; pad yaw from rvec | Heading of the motion in all four quadrants |
| Altitude | tz | p_rel = R_world_body · (R_body_cam · tvec + p_cam), altitude = −p_rel,z | Stays correct while the drone tilts to follow the pad (at 4 m a 20° tilt shifts the apparent pad by ~1.5 m) |
| Speed reference | phone speedometer on the vehicle | target ground velocity and relative velocity reported separately | The two are equal only while the drone hovers |
The distance formula is a case where exact notation matters. Coded literally as printed, it
is undefined whenever the pad moves away from the image centre (E1 below). The 2019 Table I
speeds match the phone's readings, so the implementation evidently used a plain distance and
only the typeset formula differs. The paper variant in this repo codes the printed formulas
literally so the effect can be measured.
ros2_ws/src/mtl_landing) sim_world ──/camera/image_raw──▶ aruco_detector ──/pad/pose──▶ target_estimator
▲ └──/drone/odom (flight-controller state)──────────────▶ │ mode: paper|naive|
│ │ compensated|corrected
└──/drone/cmd_vel, /drone/motors◀── landing_controller ◀──/target/estimate
flight_logger records truth + all estimators + debug frames → results/ros_run_*.json
core/ is pure Python (numpy + OpenCV) and shared by the ROS nodes, the batch runner and
the sim lab. core/pipeline.py is the one closed loop (camera → detector → estimators →
controller → physics) that the batch runner, the sim lab and the speed sweep all drive.
Estimators implement core.estimation.Estimator and return a typed core.state.Estimate;
the estimator node sends that same Estimate to the controller node on /target/estimate.mode picks the one that
flies: paper (printed formulas, read literally), naive (plain distance, camera frame),
compensated (tilt compensation) and corrected (tilt compensation + Kalman filter).docker build -f docker/Dockerfile -t mtl_landing:jazzy . # once
docker/run_lab.sh # interactive sim lab on http://localhost:8765
docker/run_ros_sim.sh weave corrected # full ROS 2 graph, one landing
docker/run_ros_sim.sh straight paper # same, flown with the printed formulas read literally
# batch experiments (core runner, same physics/detector/estimators), inside the image
docker run --rm -v $PWD/ros2_ws/src:/ws/src -v $PWD/tools:/tools -v $PWD/results:/results \
-e PYTHONPATH=/ws/src/mtl_landing --entrypoint bash mtl_landing:jazzy \
-c "python3 /tools/experiments.py all"
python3 tools/export_blog_replay.py <out_dir> straight_corrected weave_corrected # replay data for the blog player
# unit tests (equation properties, detector accuracy on rendered images)
docker run --rm -v $PWD/ros2_ws/src:/ws/src -e PYTHONPATH=/ws/src/mtl_landing \
-w /ws/src/mtl_landing --entrypoint bash mtl_landing:jazzy -c "python3 -m pytest -q test"
run_ros_sim.sh runs each container with --network none and its own ROS_DOMAIN_ID.
Without that, parallel runs discover each other over DDS and mix their /clock topics.
On a machine with ROS 2 Jazzy installed you can skip Docker: colcon build the workspace
and run ros2 launch mtl_landing landing_sim.launch.py scenario:=circle mode:=corrected.
RViz can show the /tf frames (world, base_link, pad) and /camera/debug.
docker/run_lab.sh starts a local web UI on http://localhost:8765. It runs the same
simulator, ArUco detector, estimators and landing controller live, with every parameter
on a slider:
Most knobs apply immediately; the ones marked "on reset" apply after Reset. The page shows the onboard camera with detections, a top view with every estimator's guess of the pad position, and rolling charts of height, offset and relative speed (truth against each estimator). Pause, Step (0.1 s) and simulation speed control are in the header.
.github/workflows/deploy-sim-lab.yml deploys the lab to a k3s cluster over SSH:
main: also builds docker/lab.Dockerfile (no ROS, just numpy and OpenCV)
to ghcr.io/<owner>/sim-lab:<sha>, then runs helm upgrade with the chart in
deploy/helm/sim-lab.The chart adds its own Gateway and HTTPRoute on k3s's Traefik. With a Cloudflare tunnel pointed at the host's port 80, adding a public hostname to the tunnel is the only network change needed.
The server details (SSH target, public hostname, where to copy the image pull secret
from) come from secrets on a deploy environment; the header of the workflow lists them.
They are secrets rather than variables because Actions logs of a public repo are public,
and only secrets are masked.
The hosted lab runs with LAB_PUBLIC=1. Everyone shares one simulation, so the costly
knobs are capped, viewers are capped at 20, and nothing is simulated while no one has
the page open.
Numbers from results/speed_sweep.json and results/monte_carlo.json (re-run with
tools/experiments.py all 4).
E1: speed sweep (paper Table I analogue). The drone hovers 3.5 m above the deck and the vehicle drives underneath at 1–9 m/s, 0.6 m off-axis, 3 seeds per speed. Every estimator sees identical images.
| Estimator | Mean reported speed for a 1 → 9 m/s pad | RMSE | Undefined samples |
|---|---|---|---|
| Printed formulas, read literally | 9.3 → 20.6 m/s | 9.5–13.8 m/s | 41–52 % |
| Plain distance (camera frame) | 1.4 → 8.7 m/s | 0.56–0.98 m/s | 0 % |
| Tilt-compensated | 1.2 → 8.9 m/s | 0.48–0.95 m/s | 0 % |
| Tilt-compensated + Kalman filter | 1.06 → 9.0 m/s | 0.07–0.14 m/s | filter warm-up only |
E2: estimation in flight. These are all detections from the 28 corrected-mode closed-loop flights (~22 k frames). Every estimator was evaluated on the same frames.
| Estimator | Position error p50 / p95 | Speed MAE | Altitude RMSE |
|---|---|---|---|
| Camera frame (printed / plain distance) | 21 / 96 cm | 3.79 / 0.66 m/s | 10.8 cm |
| Tilt-compensated | 1.4 / 5.5 cm | 0.15 m/s | 2.5 cm |
| Tilt-compensated + Kalman filter | 2.2 / 6.4 cm | 0.07 m/s | 1.9 cm |
The camera-frame position error grows from ~3 cm when level to over 1.5 m at 20–30° of tilt.
E3: closed-loop landings. The runs cover 7 scenarios × 4 seeds per estimator. A run counts as landed only if it ends on the pad, ≤ 0.35 m from centre, with vertical impact ≤ 2.2 m/s and horizontal slip ≤ 0.6 m/s.
| Flown by | Landed | Notes |
|---|---|---|
| Printed formulas (camera frame) | 0 / 28 | loses the pad once the drone tilts |
| Plain distance (camera frame) | 0 / 28 | loses the pad once the drone tilts |
| Tilt-compensated | 6 / 28 | mostly abort/retry timeouts |
| Tilt-compensated + Kalman filter | 7 / 28 | static 3/4, straight 2/4, accelerating 1/4, circle 1/4; weave, stop-and-go and fast 0/4 |
Estimation is solid. The weak point is the landing controller on manoeuvring targets: the pad shifts or brakes during the final hover, the camera loses it, and the drone aborts. That is the open work item.
Pad. The paper gives no marker sizes. With a 10 cm centre marker the camera lost the pad in the last 30 cm unless the drone was within ~3 cm of centre, so the pad uses 32 / 20 / 16 cm markers. Between 1.2 and 4 m the pad is detected in 86–90 % of frames. Below 0.5 m the small marker carries almost all detections.