From db18fd79d91b24e9d2c938d0ee628c4133a49af2 Mon Sep 17 00:00:00 2001 From: 1Feishu <1291113686@qq.com> Date: Sun, 2 Nov 2025 21:56:50 +0800 Subject: [PATCH 1/4] new --- rmcs_ws/src/rmcs_bringup/config/flight.yaml | 178 +++++++++++++ rmcs_ws/src/rmcs_core/plugins.xml | 3 + .../gimbal/hero_gimbal_controller.cpp | 3 +- .../gimbal/simple_gimbal_controller.cpp | 3 +- .../gimbal/two_axis_gimbal_solver.hpp | 26 +- rmcs_ws/src/rmcs_core/src/hardware/flight.cpp | 243 ++++++++++++++++++ 6 files changed, 452 insertions(+), 4 deletions(-) create mode 100644 rmcs_ws/src/rmcs_bringup/config/flight.yaml create mode 100644 rmcs_ws/src/rmcs_core/src/hardware/flight.cpp diff --git a/rmcs_ws/src/rmcs_bringup/config/flight.yaml b/rmcs_ws/src/rmcs_bringup/config/flight.yaml new file mode 100644 index 000000000..183835e74 --- /dev/null +++ b/rmcs_ws/src/rmcs_bringup/config/flight.yaml @@ -0,0 +1,178 @@ +rmcs_executor: + ros__parameters: + update_rate: 1000.0 + components: + - rmcs_core::hardware::Flight -> flight_hardware + - rmcs_core::referee::Status -> referee_status + + - rmcs_core::controller::gimbal::SimpleGimbalController -> gimbal_controller + - rmcs_core::controller::pid::ErrorPidController -> yaw_angle_pid_controller + - rmcs_core::controller::pid::PidController -> yaw_velocity_pid_controller + - rmcs_core::controller::pid::ErrorPidController -> pitch_angle_pid_controller + - rmcs_core::controller::pid::PidController -> pitch_velocity_pid_controller + + - rmcs_core::controller::shooting::FrictionWheelController -> friction_wheel_controller + - rmcs_core::controller::shooting::HeatController -> heat_controller + - rmcs_core::controller::shooting::BulletFeederController17mm -> bullet_feeder_controller + - rmcs_core::controller::pid::PidController -> left_friction_velocity_pid_controller + - rmcs_core::controller::pid::PidController -> right_friction_velocity_pid_controller + - rmcs_core::controller::pid::PidController -> bullet_feeder_velocity_pid_controller + + # - rmcs_core::broadcaster::TfBroadcaster -> tf_broadcaster + + - rmcs_core::referee::command::Interaction -> referee_interaction + #- rmcs_core::referee::command::interaction::Ui -> referee_ui + #- rmcs_core::referee::app::ui::Flight -> referee_ui_flight + + - rmcs_core::referee::Command -> referee_command + + # - rmcs_auto_aim::AutoAimInitializer -> auto_aim_initializer + # - rmcs_auto_aim::AutoAimController -> auto_aim_controller + +flight_hardware: + ros__parameters: + usb_pid: -1 + yaw_motor_zero_point: 0 + pitch_motor_zero_point: 0 + +referee_status: + ros__parameters: + path: /dev/ttyUSB0 + +gimbal_controller: + ros__parameters: + upper_limit: -0.4598 + lower_limit: 0.4362 + yaw_upper_limit: 0.872665 + yaw_lower_limit: -0.872665 + +yaw_angle_pid_controller: + ros__parameters: + measurement: /gimbal/yaw/control_angle_error + control: /gimbal/yaw/control_velocity + kp: 3.0 + ki: 0.0 + kd: 0.0 + +yaw_velocity_pid_controller: + ros__parameters: + measurement: /gimbal/yaw/velocity_imu + setpoint: /gimbal/yaw/control_velocity + control: /gimbal/yaw/control_torque + kp: 0.5 + ki: 0.0 + kd: 0.0 + +pitch_angle_pid_controller: + ros__parameters: + measurement: /gimbal/pitch/control_angle_error + control: /gimbal/pitch/control_velocity + kp: 3.0 + ki: 0.0 + kd: 0.0 + +pitch_velocity_pid_controller: + ros__parameters: + measurement: /gimbal/pitch/velocity_imu + setpoint: /gimbal/pitch/control_velocity + control: /gimbal/pitch/control_torque + kp: 0.5 + ki: 0.0 + kd: 0.0 + +friction_wheel_controller: + ros__parameters: + friction_wheels: + - /gimbal/left_friction + - /gimbal/right_friction + friction_velocities: + - 740.0 + - 740.0 + friction_soft_start_stop_time: 1.0 + +heat_controller: + ros__parameters: + heat_per_shot: 10000 + reserved_heat: 0 + +bullet_feeder_controller: + ros__parameters: + bullets_per_feeder_turn: 8.0 + shot_frequency: 20.0 + safe_shot_frequency: 10.0 + eject_frequency: 10.0 + eject_time: 0.05 + deep_eject_frequency: 5.0 + deep_eject_time: 0.2 + single_shot_max_stop_delay: 2.0 + +shooting_recorder: + ros__parameters: + friction_wheel_count: 2 + # 1: trigger, 2: timing + log_mode: 1 + +left_friction_velocity_pid_controller: + ros__parameters: + measurement: /gimbal/left_friction/velocity + setpoint: /gimbal/left_friction/control_velocity + control: /gimbal/left_friction/control_torque + kp: 0.003436926 + ki: 0.00 + kd: 0.009373434 + +right_friction_velocity_pid_controller: + ros__parameters: + measurement: /gimbal/right_friction/velocity + setpoint: /gimbal/right_friction/control_velocity + control: /gimbal/right_friction/control_torque + kp: 0.003436926 + ki: 0.00 + kd: 0.009373434 + +bullet_feeder_velocity_pid_controller: + ros__parameters: + measurement: /gimbal/bullet_feeder/velocity + setpoint: /gimbal/bullet_feeder/control_velocity + control: /gimbal/bullet_feeder/control_torque + kp: 0.583 + ki: 0.0 + kd: 0.0 + +auto_aim_controller: + ros__parameters: + # capture + use_video: false # If true, use video stream instead of camera. + video_path: "/workspaces/RMCS/rmcs_ws/resources/1.avi" + exposure_time: 3 + invert_image: false + # identifier + armor_model_path: "/models/mlp.onnx" + # pnp + fx: 1.722231837421459e+03 + fy: 1.724876404292754e+03 + cx: 7.013056440882832e+02 + cy: 5.645821718351237e+02 + k1: -0.064232403853946 + k2: -0.087667493884102 + k3: 0.792381808294582 + # tracker + armor_predict_duration: 500 + # controller + gimbal_predict_duration: 100 + yaw_error: 0.02 + pitch_error: -0.01 + shoot_velocity: 28.0 + predict_sec: 0.095 + # etc + buff_predict_duration: 200 + buff_model_path: "/models/buff_nocolor_v6.onnx" + omni_exposure: 1000.0 + record_fps: 120 + debug: false # Setup in actual using.Debug mode is used when referee is not ready + debug_color: 0 # 0 For blue while 1 for red. mine + debug_robot_id: 4 + debug_buff_mode: false + record: true + raw_img_pub: false # Set false in actual use + image_viewer_type: 2 \ No newline at end of file diff --git a/rmcs_ws/src/rmcs_core/plugins.xml b/rmcs_ws/src/rmcs_core/plugins.xml index ac7d1368b..4d3bb141b 100644 --- a/rmcs_ws/src/rmcs_core/plugins.xml +++ b/rmcs_ws/src/rmcs_core/plugins.xml @@ -8,6 +8,9 @@ Test plugin. + + Test plugin. + Test plugin. diff --git a/rmcs_ws/src/rmcs_core/src/controller/gimbal/hero_gimbal_controller.cpp b/rmcs_ws/src/rmcs_core/src/controller/gimbal/hero_gimbal_controller.cpp index f20bd98fe..65417862b 100644 --- a/rmcs_ws/src/rmcs_core/src/controller/gimbal/hero_gimbal_controller.cpp +++ b/rmcs_ws/src/rmcs_core/src/controller/gimbal/hero_gimbal_controller.cpp @@ -25,7 +25,8 @@ class HeroGimbalController rclcpp::NodeOptions{}.automatically_declare_parameters_from_overrides(true)) , imu_gimbal_solver( *this, get_parameter("upper_limit").as_double(), - get_parameter("lower_limit").as_double()) + get_parameter("lower_limit").as_double(),get_parameter("yaw_upper_limit").as_double() + ,get_parameter("yaw_lower_limit").as_double()) , encoder_gimbal_solver( *this, get_parameter("upper_limit").as_double(), get_parameter("lower_limit").as_double()) { diff --git a/rmcs_ws/src/rmcs_core/src/controller/gimbal/simple_gimbal_controller.cpp b/rmcs_ws/src/rmcs_core/src/controller/gimbal/simple_gimbal_controller.cpp index d27704d52..517dc0fcf 100644 --- a/rmcs_ws/src/rmcs_core/src/controller/gimbal/simple_gimbal_controller.cpp +++ b/rmcs_ws/src/rmcs_core/src/controller/gimbal/simple_gimbal_controller.cpp @@ -22,7 +22,8 @@ class SimpleGimbalController rclcpp::NodeOptions{}.automatically_declare_parameters_from_overrides(true)) , two_axis_gimbal_solver( *this, get_parameter("upper_limit").as_double(), - get_parameter("lower_limit").as_double()) { + get_parameter("lower_limit").as_double(),get_parameter("yaw_upper_limit").as_double() + ,get_parameter("yaw_lower_limit").as_double()) { register_input("/remote/joystick/left", joystick_left_); register_input("/remote/switch/right", switch_right_); diff --git a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp index 5d937aae0..ae590f103 100644 --- a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp +++ b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp @@ -26,11 +26,14 @@ class TwoAxisGimbalSolver { }; public: - TwoAxisGimbalSolver(rmcs_executor::Component& component, double upper_limit, double lower_limit) + TwoAxisGimbalSolver(rmcs_executor::Component& component, double upper_limit, double lower_limit,double yaw_upper_limit, double yaw_lower_limit) : upper_limit_(std::cos(upper_limit), -std::sin(upper_limit)) - , lower_limit_(std::cos(lower_limit), -std::sin(lower_limit)) { + , lower_limit_(std::cos(lower_limit), -std::sin(lower_limit)) + , yaw_upper_limit_(std::cos(yaw_upper_limit),-std::sin(yaw_upper_limit)) + , yaw_lower_limit_(std::cos(yaw_lower_limit),-std::sin(yaw_lower_limit)){ component.register_input("/gimbal/pitch/angle", gimbal_pitch_angle_); + component.register_input("/gimbal/yaw/angle", gimbal_yaw_angle_); component.register_input("/tf", tf_); } @@ -167,6 +170,23 @@ class TwoAxisGimbalSolver { *control_direction << upper_limit_.x() * projection, upper_limit_.y(); else if (z < lower_limit_.y()) *control_direction << lower_limit_.x() * projection, lower_limit_.y(); + + const auto& [x_,y_,z_] = *control_direction; + + Eigen::Vector2d yaw_projection{x_,y_}; + double yaw_norm = yaw_projection.norm(); + if (yaw_norm > 0) + yaw_projection /=yaw_norm; + else{ + control_enabled_ =false; + return; + } + + if (y_ > yaw_upper_limit_.y()) + *control_direction << yaw_upper_limit_.x() * yaw_projection.x(), yaw_upper_limit_.y(), z_; + else if (y_ < yaw_lower_limit_.y()) + *control_direction << yaw_lower_limit_.x() * yaw_projection.x(), yaw_lower_limit_.y(), z_; + } static AngleError calculate_control_errors( @@ -185,8 +205,10 @@ class TwoAxisGimbalSolver { static constexpr double nan_ = std::numeric_limits::quiet_NaN(); const Eigen::Vector2d upper_limit_, lower_limit_; + const Eigen::Vector2d yaw_upper_limit_, yaw_lower_limit_; rmcs_executor::Component::InputInterface gimbal_pitch_angle_; + rmcs_executor::Component::InputInterface gimbal_yaw_angle_; rmcs_executor::Component::InputInterface tf_; OdomImu::DirectionVector yaw_axis_filtered_{Eigen::Vector3d::UnitZ()}; diff --git a/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp b/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp new file mode 100644 index 000000000..af218545e --- /dev/null +++ b/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp @@ -0,0 +1,243 @@ +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "hardware/device/bmi088.hpp" +#include "hardware/device/dji_motor.hpp" +#include "hardware/device/dr16.hpp" +#include "hardware/device/lk_motor.hpp" +#include "librmcs/utility/ring_buffer.hpp" + +namespace rmcs_core::hardware { + +class Flight + : public rmcs_executor::Component + , public rclcpp::Node + , private librmcs::client::CBoard { +public: + Flight() + : Node{get_component_name(), rclcpp::NodeOptions{}.automatically_declare_parameters_from_overrides(true)} + , librmcs::client::CBoard{static_cast(get_parameter("usb_pid").as_int())} + , logger_(get_logger()) + , flight_command_( + create_partner_component(get_component_name() + "_command", *this)) + , gimbal_yaw_motor_(*this, *flight_command_, "/gimbal/yaw") + , gimbal_pitch_motor_(*this, *flight_command_, "/gimbal/pitch") + , gimbal_left_friction_(*this, *flight_command_, "/gimbal/left_friction") + , gimbal_right_friction_(*this, *flight_command_, "/gimbal/right_friction") + , gimbal_bullet_feeder_(*this, *flight_command_, "/gimbal/bullet_feeder") + , dr16_(*this) + , bmi088_(500.0, 0.3, 0.005) + + , transmit_buffer_(*this, 32) + , event_thread_([this]() { handle_events(); }) { + gimbal_yaw_motor_.configure( + device::LkMotor::Config{device::LkMotor::Type::MHF7015}.set_encoder_zero_point( + static_cast(get_parameter("yaw_motor_zero_point").as_int()))); + gimbal_pitch_motor_.configure( + device::LkMotor::Config{device::LkMotor::Type::MG4010E_I10}.set_encoder_zero_point( + static_cast(get_parameter("pitch_motor_zero_point").as_int()))); + gimbal_left_friction_.configure( + device::DjiMotor::Config{device::DjiMotor::Type::M3508} + .set_reversed() + .set_reduction_ratio(1.)); + gimbal_right_friction_.configure( + device::DjiMotor::Config{device::DjiMotor::Type::M3508} + + .set_reduction_ratio(1.)); + gimbal_bullet_feeder_.configure( + device::DjiMotor::Config{device::DjiMotor::Type::M2006}.enable_multi_turn_angle()); + + register_output("/gimbal/yaw/velocity_imu", gimbal_yaw_velocity_imu_); + register_output("/gimbal/pitch/velocity_imu", gimbal_pitch_velocity_imu_); + register_output("/tf", tf_); + + bmi088_.set_coordinate_mapping( + [](double x, double y, double z) { return std::make_tuple(-x, z, y); }); + + using namespace rmcs_description; + + constexpr double rotor_distance_x = 0.83637; + constexpr double rotor_distance_y = 0.83637; + + tf_->set_transform( + Eigen::Translation3d{rotor_distance_x / 2, rotor_distance_y / 2, 0}); + tf_->set_transform( + Eigen::Translation3d{rotor_distance_x / 2, -rotor_distance_y / 2, 0}); + tf_->set_transform( + Eigen::Translation3d{-rotor_distance_x / 2, rotor_distance_y / 2, 0}); + tf_->set_transform( + Eigen::Translation3d{-rotor_distance_x / 2, -rotor_distance_y / 2, 0}); + + constexpr double gimbal_center_x = 0; + constexpr double gimbal_center_y = 0; + constexpr double gimbal_center_z = 0.20552; + tf_->set_transform( + Eigen::Translation3d{gimbal_center_x, gimbal_center_y, gimbal_center_z}); + + tf_->set_transform(Eigen::Translation3d{0.0557, 0, 0.053}); + + gimbal_calibrate_subscription_ = create_subscription( + "/gimbal/calibrate", rclcpp::QoS{0}, [this](std_msgs::msg::Int32::UniquePtr&& msg) { + gimbal_calibrate_subscription_callback(std::move(msg)); + }); + register_output("/referee/serial", referee_serial_); + referee_serial_->read = [this](std::byte* buffer, size_t size) { + return referee_ring_buffer_receive_.pop_front_multi( + [&buffer](std::byte byte) { *buffer++ = byte; }, size); + }; + referee_serial_->write = [this](const std::byte* buffer, size_t size) { + transmit_buffer_.add_uart1_transmission(buffer, size); + return size; + }; + } + + ~Flight() override { + stop_handling_events(); + event_thread_.join(); + } + + void update() override { + update_motors(); + update_imu(); + dr16_.update_status(); + } + + void command_update() { + uint16_t can_commands[4]; + + transmit_buffer_.add_can1_transmission(0x141, gimbal_yaw_motor_.generate_command()); + transmit_buffer_.add_can1_transmission(0x142, gimbal_pitch_motor_.generate_command()); + + can_commands[0] = gimbal_bullet_feeder_.generate_command(); + can_commands[1] = gimbal_left_friction_.generate_command(); + can_commands[2] = gimbal_right_friction_.generate_command(); + can_commands[3] = 0; + transmit_buffer_.add_can2_transmission(0x200, std::bit_cast(can_commands)); + } + +private: + void update_motors() { + using namespace rmcs_description; + gimbal_yaw_motor_.update_status(); + tf_->set_state(gimbal_yaw_motor_.angle()); + + gimbal_pitch_motor_.update_status(); + tf_->set_state(gimbal_pitch_motor_.angle()); + + gimbal_bullet_feeder_.update_status(); + gimbal_left_friction_.update_status(); + gimbal_right_friction_.update_status(); + } + + void update_imu() { + bmi088_.update_status(); + Eigen::Quaterniond gimbal_imu_pose{bmi088_.q0(), bmi088_.q1(), bmi088_.q2(), bmi088_.q3()}; + tf_->set_transform( + gimbal_imu_pose.conjugate()); + + *gimbal_yaw_velocity_imu_ = bmi088_.gz(); + *gimbal_pitch_velocity_imu_ = bmi088_.gy(); + } + void gimbal_calibrate_subscription_callback(std_msgs::msg::Int32::UniquePtr) { + RCLCPP_INFO( + logger_, "[gimbal calibration] New yaw offset: %ld", + gimbal_yaw_motor_.calibrate_zero_point()); + RCLCPP_INFO( + logger_, "[gimbal calibration] New pitch offset: %ld", + gimbal_pitch_motor_.calibrate_zero_point()); + } + +protected: + void can1_receive_callback( + uint32_t can_id, uint64_t can_data, bool is_extended_can_id, bool is_remote_transmission, + uint8_t can_data_length) override { + if (is_extended_can_id || is_remote_transmission || can_data_length < 8) [[unlikely]] + return; + + if (can_id == 0x141) { + gimbal_yaw_motor_.store_status(can_data); + } else if (can_id == 0x142) { + gimbal_pitch_motor_.store_status(can_data); + } + } + + void can2_receive_callback( + uint32_t can_id, uint64_t can_data, bool is_extended_can_id, bool is_remote_transmission, + uint8_t can_data_length) override { + if (is_remote_transmission || is_extended_can_id || can_data_length < 8) [[unlikely]] + return; + if (can_id == 0x201) { + gimbal_bullet_feeder_.store_status(can_data); + } else if (can_id == 0x203) { + gimbal_right_friction_.store_status(can_data); + } else if (can_id == 0x202) { + gimbal_left_friction_.store_status(can_data); + } + } + + void uart1_receive_callback(const std::byte* uart_data, uint8_t uart_data_length) override { + referee_ring_buffer_receive_.emplace_back_multi( + [&uart_data](std::byte* storage) { *storage = *uart_data++; }, uart_data_length); + } + + void dbus_receive_callback(const std::byte* uart_data, uint8_t uart_data_length) override { + dr16_.store_status(uart_data, uart_data_length); + } + + void accelerometer_receive_callback(int16_t x, int16_t y, int16_t z) override { + bmi088_.store_accelerometer_status(x, y, z); + } + + void gyroscope_receive_callback(int16_t x, int16_t y, int16_t z) override { + bmi088_.store_gyroscope_status(x, y, z); + } + +private: + rclcpp::Logger logger_; + class FlightCommand : public rmcs_executor::Component { + public: + explicit FlightCommand(Flight& flight) + : flight_(flight) {} + void update() override { flight_.command_update(); } + Flight& flight_; + }; + + std::shared_ptr flight_command_; + rclcpp::Subscription::SharedPtr gimbal_calibrate_subscription_; + device::LkMotor gimbal_yaw_motor_; + device::LkMotor gimbal_pitch_motor_; + + device::DjiMotor gimbal_left_friction_; + device::DjiMotor gimbal_right_friction_; + device::DjiMotor gimbal_bullet_feeder_; + + device::Dr16 dr16_; + device::Bmi088 bmi088_; + + OutputInterface gimbal_yaw_velocity_imu_; + OutputInterface gimbal_pitch_velocity_imu_; + + OutputInterface tf_; + + librmcs::utility::RingBuffer referee_ring_buffer_receive_{256}; + OutputInterface referee_serial_; + + librmcs::client::CBoard::TransmitBuffer transmit_buffer_; + std::thread event_thread_; +}; +} // namespace rmcs_core::hardware + +#include + +PLUGINLIB_EXPORT_CLASS(rmcs_core::hardware::Flight, rmcs_executor::Component) \ No newline at end of file From bb555662320d8251271852d03ad9ffa5be2826be Mon Sep 17 00:00:00 2001 From: Embers-of-the-Fire Date: Sat, 14 Feb 2026 19:16:30 +0800 Subject: [PATCH 2/4] Merge downstream to branch `gimbal` --- .clangd | 4 + .github/workflows/update-image.yml | 49 ------------ .gitmodules | 6 ++ .script/complete/_play-autoaim | 7 ++ .script/complete/_set-remote | 22 ++++++ .script/foxglove | 5 ++ .script/play-autoaim | 74 +++++++++++++++++++ .script/sync-remote | 2 +- .script/template/env_setup.bash | 2 +- .script/template/env_setup.zsh | 8 +- DEVELOPMENT.md | 8 ++ Dockerfile | 42 ++++++++--- README.md | 2 + docker-compose.yml | 13 +--- ip.conf | 1 + rmcs_ws/src/rmcs_bringup/config/flight.yaml | 62 ++++++++++------ rmcs_ws/src/rmcs_core/package.xml | 1 + rmcs_ws/src/rmcs_core/plugins.xml | 3 + .../gimbal/two_axis_gimbal_solver.hpp | 23 ++++-- rmcs_ws/src/rmcs_core/src/hardware/flight.cpp | 31 ++++---- .../include/rmcs_executor/component.hpp | 32 ++++---- 21 files changed, 268 insertions(+), 129 deletions(-) create mode 100644 .clangd delete mode 100644 .github/workflows/update-image.yml create mode 100644 .script/complete/_play-autoaim create mode 100644 .script/complete/_set-remote create mode 100755 .script/foxglove create mode 100755 .script/play-autoaim create mode 100644 DEVELOPMENT.md create mode 100644 ip.conf diff --git a/.clangd b/.clangd new file mode 100644 index 000000000..5ba2064ee --- /dev/null +++ b/.clangd @@ -0,0 +1,4 @@ +CompileFlags: + Add: + - --target=x86_64-linux-gnu + \ No newline at end of file diff --git a/.github/workflows/update-image.yml b/.github/workflows/update-image.yml deleted file mode 100644 index 71c5538fe..000000000 --- a/.github/workflows/update-image.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build and Push RMCS Images - -on: - workflow_dispatch: - push: - paths: - - 'Dockerfile' - branches: - - main - -jobs: - build-and-push: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up SSH keys - run: | - echo "${{ secrets.CONTAINER_ID_RSA }}" > .ssh/id_rsa - echo "${{ secrets.CONTAINER_ID_RSA_PUB }}" > .ssh/id_rsa.pub - chmod 600 .ssh/id_rsa - chmod 644 .ssh/id_rsa.pub - - - name: Build and push rmcs-develop:latest - uses: docker/build-push-action@v6 - with: - context: . - push: true - target: rmcs-develop - tags: qzhhhi/rmcs-develop:latest - - - name: Build and push rmcs-runtime:latest - uses: docker/build-push-action@v6 - with: - context: . - push: true - target: rmcs-runtime - tags: qzhhhi/rmcs-runtime:latest diff --git a/.gitmodules b/.gitmodules index d4d71be28..6706448a9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,9 @@ [submodule "rmcs_ws/src/serial"] path = rmcs_ws/src/serial url = https://github.com/Alliance-Algorithm/ros2-serial.git +[submodule "rmcs_ws/src/rmcs_slam"] + path = rmcs_ws/src/rmcs_slam + url = https://github.com/Alliance-Algorithm/rmcs_slam +[submodule "rmcs_ws/src/livox_ros_driver2"] + path = rmcs_ws/src/livox_ros_driver2 + url = https://github.com/Alliance-Algorithm/livox_ros_driver2.git diff --git a/.script/complete/_play-autoaim b/.script/complete/_play-autoaim new file mode 100644 index 000000000..025a0974c --- /dev/null +++ b/.script/complete/_play-autoaim @@ -0,0 +1,7 @@ +#compdef play-autoaim + +_arguments \ + '--user[指定远程用户名]:用户名:' \ + '--remote[从设备拉取 SDP 到容器]' \ + '--no-copy[跳过容器到宿主机的拷贝]'\ + '--ip[监视端的 IP 地址]' diff --git a/.script/complete/_set-remote b/.script/complete/_set-remote new file mode 100644 index 000000000..e5599c3f4 --- /dev/null +++ b/.script/complete/_set-remote @@ -0,0 +1,22 @@ +#compdef set-remote + +_remote_hosts() { + local hosts=( + "169.254.233.233" + "alliance-sentry.local" + "alliance-infantry.local" + "alliance-hero.local" + ) + + # 从 ~/.ssh/config 提取最近使用的 HostName + if [[ -f ~/.ssh/config ]]; then + local extracted + extracted=(${(f)"$(grep -A1 'Host remote' ~/.ssh/config | grep HostName | awk '{print $2}')"}) + hosts+=(${extracted}) + fi + + compadd -- $hosts +} + +_arguments \ + '1:远程主机地址:_remote_hosts' diff --git a/.script/foxglove b/.script/foxglove new file mode 100755 index 000000000..ba845e52e --- /dev/null +++ b/.script/foxglove @@ -0,0 +1,5 @@ +#! /bin/bash + +source ~/env_setup.bash + +ros2 launch foxglove_bridge foxglove_bridge_launch.xml port:=8765 diff --git a/.script/play-autoaim b/.script/play-autoaim new file mode 100755 index 000000000..b9a33f0f5 --- /dev/null +++ b/.script/play-autoaim @@ -0,0 +1,74 @@ +#!/bin/sh + +set -e + +MONITOR_USER="" +MONITOR_PLAYER="vlc" +MONITOR_HOST="localhost" + +SDP_PATH="/tmp/auto_aim.sdp" + +USE_REMOTE=false +SKIP_COPY=false + +# 参数解析 +while [ "$#" -gt 0 ]; do + case "$1" in + --user) + shift + [ -z "$1" ] && echo "❌ --user 参数缺失" && exit 1 + MONITOR_USER="$1" + ;; + --remote) + USE_REMOTE=true + ;; + --no-copy) + SKIP_COPY=true + ;; + --ip) + shift + [ -z "$1" ] && echo "❌ --ip 参数缺失" && exit 1 + MONITOR_HOST="$1" + ;; + *) + echo "未知参数: $1" + echo "用法: $0 --user <用户名> [--remote] [--no-copy]" + exit 1 + ;; + esac + shift +done + +if [ -z "$MONITOR_USER" ]; then + echo "❌ 缺少 --user 参数" + echo "用法: play-autoaim --user <用户名> [--remote] [--no-copy]" + exit 1 +fi + +if [ "$USE_REMOTE" = true ]; then + scp remote:${SDP_PATH} ${SDP_PATH} + if [ $? -ne 0 ]; then + echo " 从 remote 拷贝失败。是否继续?(y/n)" + read -r answer + case "$answer" in + [Yy]*) echo " 继续执行后续操作…" ;; + *) + echo " 已取消。" + exit 1 + ;; + esac + fi +fi + +if [ "$SKIP_COPY" = false ]; then + scp "${SDP_PATH}" "${MONITOR_USER}@${MONITOR_HOST}:${SDP_PATH}" + if [ $? -ne 0 ]; then + echo " scp 拷贝失败" + exit 1 + fi + echo " 文件已拷贝到宿主机:${SDP_PATH}" +else + echo " 跳过拷贝,直接打开远程 SDP" +fi + +ssh -f "${MONITOR_USER}@${MONITOR_HOST}" "DISPLAY=:0 ${MONITOR_PLAYER} '${SDP_PATH}' --network-caching=50" diff --git a/.script/sync-remote b/.script/sync-remote index f9c915fc8..7d62782b9 100755 --- a/.script/sync-remote +++ b/.script/sync-remote @@ -8,7 +8,7 @@ import subprocess from colorama import Fore, Style -SRC_DIR = "${RMCS_PATH}/rmcs_ws/install" +SRC_DIR = os.getenv("RMCS_PATH") + "/rmcs_ws/install" DST_DIR = "ssh://remote//rmcs_install" SOCKET_PATH = "/tmp/sync-remote" diff --git a/.script/template/env_setup.bash b/.script/template/env_setup.bash index f8ab1dd27..341647d3b 100644 --- a/.script/template/env_setup.bash +++ b/.script/template/env_setup.bash @@ -1,6 +1,6 @@ #!/bin/bash -export ROS_LOCALHOST_ONLY=1 +export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST export RCUTILS_COLORIZED_OUTPUT=1 export RMCS_PATH="/workspaces/RMCS" diff --git a/.script/template/env_setup.zsh b/.script/template/env_setup.zsh index 335250c90..fab840df0 100644 --- a/.script/template/env_setup.zsh +++ b/.script/template/env_setup.zsh @@ -1,7 +1,7 @@ -#!/bin/bash +#!/bin/zsh +export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST export RCUTILS_COLORIZED_OUTPUT=1 -export ROS_LOCALHOST_ONLY=1 export RMCS_PATH="/workspaces/RMCS" source /opt/ros/jazzy/setup.zsh @@ -16,3 +16,7 @@ eval "$(register-python-argcomplete ros2)" eval "$(register-python-argcomplete colcon)" export RMCS_ROBOT_TYPE="" + +fpath=(${RMCS_PATH}/.script/complete $fpath) +autoload -Uz compinit +compinit diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..c1158a378 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,8 @@ +# Development of UAV + +This branch contains runtime code for the UAV. + +## External Dependencies + +Flight controller takes advantage of the SLAM runtime [`rmcs_slam`](https://Alliance-Algorithm/rmcs_slam). +Remember to sync the submodule. diff --git a/Dockerfile b/Dockerfile index c4a67af1a..22ba59bda 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ vim wget curl unzip \ zsh screen tmux \ usbutils net-tools iputils-ping \ + gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ ripgrep htop fzf \ libusb-1.0-0-dev \ libeigen3-dev \ @@ -29,12 +30,18 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libceres-dev \ ros-$ROS_DISTRO-rviz2 \ ros-$ROS_DISTRO-foxglove-bridge \ + ros-$ROS_DISTRO-pcl-conversions \ + ros-$ROS_DISTRO-pcl-ros \ + libpcl-ros-dev \ + libpcl-dev \ + libgomp-dev \ + libomp-dev \ + libomp-22-dev \ dotnet-sdk-8.0 \ ros-$ROS_DISTRO-pcl-ros ros-$ROS_DISTRO-pcl-conversions ros-$ROS_DISTRO-pcl-msgs && \ apt-get autoremove -y && apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* - # Install openvino runtime RUN wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB && \ apt-key add ./GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB && \ @@ -81,19 +88,25 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ openssh-client \ lsb-release software-properties-common gnupg sudo \ python3-colorama python3-dpkt && \ - wget -O ./llvm-snapshot.gpg.key https://apt.llvm.org/llvm-snapshot.gpg.key && \ - apt-key add ./llvm-snapshot.gpg.key && \ - rm ./llvm-snapshot.gpg.key && \ - echo "deb https://apt.llvm.org/noble/ llvm-toolchain-noble main" > /etc/apt/sources.list.d/llvm-apt.list && \ - apt-get update && \ - version=`apt-cache search clangd- | grep clangd- | awk -F' ' '{print $1}' | sort -V | tail -1 | cut -d- -f2` && \ - apt-get install -y --no-install-recommends clangd-$version && \ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 50 && \ update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50 && \ - update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-$version 50 && \ apt-get autoremove -y && apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* +# Install latest stable llvm-toolchain +RUN mkdir -p /etc/apt/keyrings && \ + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | gpg -o /etc/apt/keyrings/llvm-snapshot.gpg --dearmor && \ + echo "deb [signed-by=/etc/apt/keyrings/llvm-snapshot.gpg] https://mirrors.tuna.tsinghua.edu.cn/llvm-apt/noble/ llvm-toolchain-noble-22 main" \ + | tee /etc/apt/sources.list.d/llvm.list && \ + apt-get update && \ + apt-get install -y --no-install-recommends clang-22 clangd-22 clang-format-22 lldb-22 && \ + update-alternatives --install /usr/bin/clang clang /usr/bin/clang-22 100 && \ + update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-22 100 && \ + update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-22 100 && \ + update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-22 100 && \ + update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-22 100 && \ + apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* + # Generate/load ssh key and setup unison RUN --mount=type=bind,target=/tmp/.ssh,source=.ssh,readonly=false \ cd /home/ubuntu && mkdir -p .ssh && \ @@ -110,6 +123,11 @@ RUN curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linu tar -C /opt -xzf nvim-linux-x86_64.tar.gz && \ rm nvim-linux-x86_64.tar.gz +# Install latest stable cmake for user ubuntu +RUN wget https://github.com/kitware/cmake/releases/download/v4.2.3/cmake-4.2.3-linux-x86_64.sh -O install.sh && \ + mkdir -p /opt/cmake/ && bash install.sh --skip-license --prefix=/opt/cmake/ --exclude-subdir && \ + rm install.sh + # Change user RUN chsh -s /bin/zsh ubuntu && \ echo "ubuntu ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers @@ -123,12 +141,14 @@ RUN sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools sed -i 's/ZSH_THEME=\"[a-z0-9\-]*\"/ZSH_THEME="af-magic"/g' ~/.zshrc && \ echo 'source ~/env_setup.zsh' >> ~/.zshrc && \ echo 'export PATH="${PATH}:/opt/nvim-linux-x86_64/bin"' >> ~/.zshrc && \ + # echo 'export PATH="${PATH}:/opt/cmake/bin"' >> ~/.zshrc && \ echo 'export PATH="${PATH}:${RMCS_PATH}/.script"' >> ~/.zshrc # Copy environment setup scripts COPY --chown=1000:1000 .script/template/env_setup.bash env_setup.bash COPY --chown=1000:1000 .script/template/env_setup.zsh env_setup.zsh + # Runtime container, will automatically launch the main program FROM rmcs-base AS rmcs-runtime @@ -150,6 +170,10 @@ RUN sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools echo 'export PATH=${PATH}:/rmcs_install/lib/rmcs_cli' >> ~/.zshrc && \ chsh -s /bin/zsh root +# Remove git proxy config to avoid issues when interacting with docker +RUN git config --global --unset http.proxy || true && \ + git config --global --unset https.proxy || true + RUN mkdir -p /rmcs_install/ COPY --chown=root:root .script/set-robot /usr/local/bin/set-robot diff --git a/README.md b/README.md index 1bb950ab6..dc365f69b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # RMCS RoboMaster Control System based on ROS2. +快速开始: [quick-start](https://github.com/Alliance-Algorithm/RMCS/wiki/Quick-Start) + ## Development ### Pre-requirements: diff --git a/docker-compose.yml b/docker-compose.yml index d59afc5bf..43ec6c74d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,11 @@ services: rmcs-develop: image: qzhhhi/rmcs-develop:latest - user: "${CONTAINER_USER}" - privileged: true - command: > - bash -c " - sudo chown -R ${CONTAINER_USER}:${CONTAINER_USER} ${CONTAINER_HOME}/.config - exec bash - " + user: "1000:1000" volumes: - /dev:/dev:bind - /tmp/.X11-unix:/tmp/.X11-unix:bind - - /run/user/1000/wayland-0:/run/user/1000/wayland-0 + - /run/user/1000/wayland-0:/run/user/1000/wayland-0:bind - ${HOST_NVIM_DIR}:${CONTAINER_HOME}/.config/nvim/:bind - .:/workspaces/RMCS:bind environment: @@ -19,5 +13,4 @@ services: - WAYLAND_DISPLAY=${WAYLAND_DISPLAY} network_mode: host tty: true - stdin_open: true - + stdin_open: true diff --git a/ip.conf b/ip.conf new file mode 100644 index 000000000..24dd82fad --- /dev/null +++ b/ip.conf @@ -0,0 +1 @@ +169.254.233.233 diff --git a/rmcs_ws/src/rmcs_bringup/config/flight.yaml b/rmcs_ws/src/rmcs_bringup/config/flight.yaml index 183835e74..f7d7ac40a 100644 --- a/rmcs_ws/src/rmcs_bringup/config/flight.yaml +++ b/rmcs_ws/src/rmcs_bringup/config/flight.yaml @@ -9,7 +9,6 @@ rmcs_executor: - rmcs_core::controller::pid::ErrorPidController -> yaw_angle_pid_controller - rmcs_core::controller::pid::PidController -> yaw_velocity_pid_controller - rmcs_core::controller::pid::ErrorPidController -> pitch_angle_pid_controller - - rmcs_core::controller::pid::PidController -> pitch_velocity_pid_controller - rmcs_core::controller::shooting::FrictionWheelController -> friction_wheel_controller - rmcs_core::controller::shooting::HeatController -> heat_controller @@ -18,8 +17,6 @@ rmcs_executor: - rmcs_core::controller::pid::PidController -> right_friction_velocity_pid_controller - rmcs_core::controller::pid::PidController -> bullet_feeder_velocity_pid_controller - # - rmcs_core::broadcaster::TfBroadcaster -> tf_broadcaster - - rmcs_core::referee::command::Interaction -> referee_interaction #- rmcs_core::referee::command::interaction::Ui -> referee_ui #- rmcs_core::referee::app::ui::Flight -> referee_ui_flight @@ -29,54 +26,70 @@ rmcs_executor: # - rmcs_auto_aim::AutoAimInitializer -> auto_aim_initializer # - rmcs_auto_aim::AutoAimController -> auto_aim_controller + - rmcs_core::broadcaster::ValueBroadcaster -> value_broadcaster + - rmcs_core::broadcaster::TfBroadcaster -> tf_broadcaster + +value_broadcaster: + ros__parameters: + forward_list: + - /gimbal/pitch/angle + - /gimbal/pitch/velocity + - /gimbal/pitch/torque + - /gimbal/pitch/control_torque + - /gimbal/pitch/control_angle_error + - /gimbal/pitch/control_velocity + - /gimbal/pitch/velocity_imu + + - /gimbal/yaw/angle + - /gimbal/yaw/velocity + - /gimbal/yaw/torque + - /gimbal/yaw/control_torque + - /gimbal/yaw/control_angle_error + - /gimbal/yaw/control_velocity + +tf_broadcaster: + ros__parameters: + tf: /tf + flight_hardware: ros__parameters: usb_pid: -1 - yaw_motor_zero_point: 0 - pitch_motor_zero_point: 0 + yaw_motor_zero_point: 4608 + pitch_motor_zero_point: 51629 referee_status: ros__parameters: - path: /dev/ttyUSB0 + path: /dev/tty0 gimbal_controller: ros__parameters: - upper_limit: -0.4598 - lower_limit: 0.4362 - yaw_upper_limit: 0.872665 - yaw_lower_limit: -0.872665 + upper_limit: -0.149 + lower_limit: 0.149 + yaw_upper_limit: -0.349 + yaw_lower_limit: 0.349 yaw_angle_pid_controller: ros__parameters: measurement: /gimbal/yaw/control_angle_error control: /gimbal/yaw/control_velocity - kp: 3.0 + kp: 8.0 ki: 0.0 - kd: 0.0 + kd: 0.0003 yaw_velocity_pid_controller: ros__parameters: measurement: /gimbal/yaw/velocity_imu setpoint: /gimbal/yaw/control_velocity control: /gimbal/yaw/control_torque - kp: 0.5 + kp: 1.5 ki: 0.0 - kd: 0.0 + kd: 0.01 pitch_angle_pid_controller: ros__parameters: measurement: /gimbal/pitch/control_angle_error control: /gimbal/pitch/control_velocity - kp: 3.0 - ki: 0.0 - kd: 0.0 - -pitch_velocity_pid_controller: - ros__parameters: - measurement: /gimbal/pitch/velocity_imu - setpoint: /gimbal/pitch/control_velocity - control: /gimbal/pitch/control_torque - kp: 0.5 + kp: 16.0 ki: 0.0 kd: 0.0 @@ -156,6 +169,7 @@ auto_aim_controller: k1: -0.064232403853946 k2: -0.087667493884102 k3: 0.792381808294582 + # tracker armor_predict_duration: 500 # controller diff --git a/rmcs_ws/src/rmcs_core/package.xml b/rmcs_ws/src/rmcs_core/package.xml index 410745520..3b401799c 100644 --- a/rmcs_ws/src/rmcs_core/package.xml +++ b/rmcs_ws/src/rmcs_core/package.xml @@ -19,6 +19,7 @@ rmcs_msgs rmcs_executor rmcs_description + rmcs_slam ament_lint_auto ament_lint_common diff --git a/rmcs_ws/src/rmcs_core/plugins.xml b/rmcs_ws/src/rmcs_core/plugins.xml index 4d3bb141b..02efadb10 100644 --- a/rmcs_ws/src/rmcs_core/plugins.xml +++ b/rmcs_ws/src/rmcs_core/plugins.xml @@ -71,6 +71,9 @@ Test plugin. + + Dynamic TF publisher for gimbal coordinate frames + Test plugin. diff --git a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp index ae590f103..b218e27f0 100644 --- a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp +++ b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp @@ -1,5 +1,6 @@ #pragma once +#include "librmcs/utility/logging.hpp" #include #include @@ -26,11 +27,13 @@ class TwoAxisGimbalSolver { }; public: - TwoAxisGimbalSolver(rmcs_executor::Component& component, double upper_limit, double lower_limit,double yaw_upper_limit, double yaw_lower_limit) + TwoAxisGimbalSolver( + rmcs_executor::Component& component, double upper_limit, double lower_limit, + double yaw_upper_limit, double yaw_lower_limit) : upper_limit_(std::cos(upper_limit), -std::sin(upper_limit)) - , lower_limit_(std::cos(lower_limit), -std::sin(lower_limit)) - , yaw_upper_limit_(std::cos(yaw_upper_limit),-std::sin(yaw_upper_limit)) - , yaw_lower_limit_(std::cos(yaw_lower_limit),-std::sin(yaw_lower_limit)){ + , lower_limit_(std::cos(lower_limit), -std::sin(lower_limit)) + , yaw_upper_limit_(std::cos(yaw_upper_limit), -std::sin(yaw_upper_limit)) + , yaw_lower_limit_(std::cos(yaw_lower_limit), -std::sin(yaw_lower_limit)) { component.register_input("/gimbal/pitch/angle", gimbal_pitch_angle_); component.register_input("/gimbal/yaw/angle", gimbal_yaw_angle_); @@ -107,6 +110,11 @@ class TwoAxisGimbalSolver { update_yaw_axis(); PitchLink::DirectionVector control_direction = operation.update(*this); + // LOG_INFO( + // "x: %f, y: %f, z: %f", control_direction->x(), control_direction->y(), + // control_direction->z()); + + if (!control_enabled_) return {nan_, nan_}; @@ -116,6 +124,7 @@ class TwoAxisGimbalSolver { if (!control_enabled_) return {nan_, nan_}; + control_direction_ = fast_tf::cast(yaw_link_to_pitch_link(control_direction_yaw_link, pitch), *tf_); return calculate_control_errors(control_direction_yaw_link, pitch); @@ -171,7 +180,7 @@ class TwoAxisGimbalSolver { else if (z < lower_limit_.y()) *control_direction << lower_limit_.x() * projection, lower_limit_.y(); - const auto& [x_,y_,z_] = *control_direction; + const auto& [x_,y_,z_] = *control_direction; Eigen::Vector2d yaw_projection{x_,y_}; double yaw_norm = yaw_projection.norm(); @@ -189,6 +198,7 @@ class TwoAxisGimbalSolver { } + static AngleError calculate_control_errors( const YawLink::DirectionVector& control_direction, const Eigen::Vector2d& pitch) { const auto& [x, y, z] = *control_direction; @@ -199,6 +209,9 @@ class TwoAxisGimbalSolver { double x_projected = std::sqrt(x * x + y * y); result.pitch_angle_error = -std::atan2(z * c - x_projected * s, z * s + x_projected * c); + // LOG_INFO("x: %f, y: %f, z: %f, yaw err: %f, pitch err: %f",x, y, + // z,result.yaw_angle_error,result.pitch_angle_error); + return result; } diff --git a/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp b/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp index af218545e..5d741135e 100644 --- a/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp +++ b/rmcs_ws/src/rmcs_core/src/hardware/flight.cpp @@ -26,7 +26,9 @@ class Flight , private librmcs::client::CBoard { public: Flight() - : Node{get_component_name(), rclcpp::NodeOptions{}.automatically_declare_parameters_from_overrides(true)} + : Node{ + get_component_name(), + rclcpp::NodeOptions{}.automatically_declare_parameters_from_overrides(true)} , librmcs::client::CBoard{static_cast(get_parameter("usb_pid").as_int())} , logger_(get_logger()) , flight_command_( @@ -42,7 +44,7 @@ class Flight , transmit_buffer_(*this, 32) , event_thread_([this]() { handle_events(); }) { gimbal_yaw_motor_.configure( - device::LkMotor::Config{device::LkMotor::Type::MHF7015}.set_encoder_zero_point( + device::LkMotor::Config{device::LkMotor::Type::MHF7015}.set_reversed().set_encoder_zero_point( static_cast(get_parameter("yaw_motor_zero_point").as_int()))); gimbal_pitch_motor_.configure( device::LkMotor::Config{device::LkMotor::Type::MG4010E_I10}.set_encoder_zero_point( @@ -53,7 +55,7 @@ class Flight .set_reduction_ratio(1.)); gimbal_right_friction_.configure( device::DjiMotor::Config{device::DjiMotor::Type::M3508} - + .set_reduction_ratio(1.)); gimbal_bullet_feeder_.configure( device::DjiMotor::Config{device::DjiMotor::Type::M2006}.enable_multi_turn_angle()); @@ -81,7 +83,7 @@ class Flight constexpr double gimbal_center_x = 0; constexpr double gimbal_center_y = 0; - constexpr double gimbal_center_z = 0.20552; + constexpr double gimbal_center_z = -0.20552; tf_->set_transform( Eigen::Translation3d{gimbal_center_x, gimbal_center_y, gimbal_center_z}); @@ -111,19 +113,22 @@ class Flight update_motors(); update_imu(); dr16_.update_status(); + } void command_update() { uint16_t can_commands[4]; - transmit_buffer_.add_can1_transmission(0x141, gimbal_yaw_motor_.generate_command()); - transmit_buffer_.add_can1_transmission(0x142, gimbal_pitch_motor_.generate_command()); + transmit_buffer_.add_can1_transmission(0x141, gimbal_yaw_motor_.generate_torque_command()); + transmit_buffer_.add_can2_transmission(0x144, gimbal_pitch_motor_.generate_command()); can_commands[0] = gimbal_bullet_feeder_.generate_command(); - can_commands[1] = gimbal_left_friction_.generate_command(); - can_commands[2] = gimbal_right_friction_.generate_command(); + can_commands[1] = gimbal_right_friction_.generate_command(); + can_commands[2] = gimbal_left_friction_.generate_command(); can_commands[3] = 0; transmit_buffer_.add_can2_transmission(0x200, std::bit_cast(can_commands)); + + transmit_buffer_.trigger_transmission(); } private: @@ -146,7 +151,7 @@ class Flight tf_->set_transform( gimbal_imu_pose.conjugate()); - *gimbal_yaw_velocity_imu_ = bmi088_.gz(); + *gimbal_yaw_velocity_imu_ = bmi088_.gz(); *gimbal_pitch_velocity_imu_ = bmi088_.gy(); } void gimbal_calibrate_subscription_callback(std_msgs::msg::Int32::UniquePtr) { @@ -167,8 +172,6 @@ class Flight if (can_id == 0x141) { gimbal_yaw_motor_.store_status(can_data); - } else if (can_id == 0x142) { - gimbal_pitch_motor_.store_status(can_data); } } @@ -180,9 +183,11 @@ class Flight if (can_id == 0x201) { gimbal_bullet_feeder_.store_status(can_data); } else if (can_id == 0x203) { - gimbal_right_friction_.store_status(can_data); - } else if (can_id == 0x202) { gimbal_left_friction_.store_status(can_data); + } else if (can_id == 0x202) { + gimbal_right_friction_.store_status(can_data); + } else if (can_id == 0x144) { + gimbal_pitch_motor_.store_status(can_data); } } diff --git a/rmcs_ws/src/rmcs_executor/include/rmcs_executor/component.hpp b/rmcs_ws/src/rmcs_executor/include/rmcs_executor/component.hpp index cba9b55fa..9155e7a9a 100644 --- a/rmcs_ws/src/rmcs_executor/include/rmcs_executor/component.hpp +++ b/rmcs_ws/src/rmcs_executor/include/rmcs_executor/component.hpp @@ -16,12 +16,12 @@ class Component { public: friend class Executor; - Component(const Component&) = delete; + Component(const Component&) = delete; Component& operator=(const Component&) = delete; - Component(Component&&) = delete; - Component& operator=(Component&&) = delete; + Component(Component&&) = delete; + Component& operator=(Component&&) = delete; - virtual ~Component(){}; + virtual ~Component() = default; virtual void before_pairing(const std::map& output_map) { (void)output_map; @@ -37,10 +37,10 @@ class Component { InputInterface() = default; - InputInterface(const InputInterface&) = delete; + InputInterface(const InputInterface&) = delete; InputInterface& operator=(const InputInterface&) = delete; - InputInterface(InputInterface&&) = delete; - InputInterface& operator=(InputInterface&&) = delete; + InputInterface(InputInterface&&) = delete; + InputInterface& operator=(InputInterface&&) = delete; ~InputInterface() { if (delete_data_when_deconstruct) { @@ -61,7 +61,7 @@ class Component { throw std::runtime_error("The interface has already been bound to somewhere"); data_pointer_ = new T(std::forward(args)...); - activated = true; + activated = true; delete_data_when_deconstruct = true; } @@ -71,7 +71,7 @@ class Component { throw std::runtime_error("The interface has already been bound to somewhere"); data_pointer_ = const_cast(&destination); - activated = true; + activated = true; } const T* operator->() const { return data_pointer_; } @@ -84,7 +84,7 @@ class Component { } T* data_pointer_ = nullptr; - bool activated = false; + bool activated = false; bool delete_data_when_deconstruct = false; }; @@ -96,10 +96,10 @@ class Component { OutputInterface() = default; - OutputInterface(const OutputInterface&) = delete; + OutputInterface(const OutputInterface&) = delete; OutputInterface& operator=(const OutputInterface&) = delete; - OutputInterface(OutputInterface&&) = delete; - OutputInterface& operator=(OutputInterface&&) = delete; + OutputInterface(OutputInterface&&) = delete; + OutputInterface& operator=(OutputInterface&&) = delete; ~OutputInterface() { if (active()) @@ -136,6 +136,7 @@ class Component { } template + requires std::constructible_from void register_output(const std::string& name, OutputInterface& interface, Args&&... args) { if (interface.active()) throw std::runtime_error("The interface has been activated"); @@ -144,6 +145,7 @@ class Component { } template + requires std::constructible_from std::shared_ptr create_partner_component(const std::string& name, Args&&... args) { initializing_component_name = name.c_str(); @@ -182,8 +184,8 @@ class Component { std::vector> partner_component_list_; - size_t dependency_count_ = 0; + std::size_t dependency_count_ = 0; std::unordered_set wanted_by_ = {}; }; -} // namespace rmcs_executor \ No newline at end of file +} // namespace rmcs_executor From f99b7ed47844667e88d78cb4dab33682e2b580cd Mon Sep 17 00:00:00 2001 From: Embers-of-the-Fire Date: Sat, 14 Feb 2026 19:21:27 +0800 Subject: [PATCH 3/4] fix: fix logging import --- .../src/controller/gimbal/two_axis_gimbal_solver.hpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp index b218e27f0..a16aa2238 100644 --- a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp +++ b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp @@ -8,8 +8,6 @@ #include #include -#include -#include #include #include #include @@ -110,10 +108,6 @@ class TwoAxisGimbalSolver { update_yaw_axis(); PitchLink::DirectionVector control_direction = operation.update(*this); - // LOG_INFO( - // "x: %f, y: %f, z: %f", control_direction->x(), control_direction->y(), - // control_direction->z()); - if (!control_enabled_) return {nan_, nan_}; @@ -124,7 +118,6 @@ class TwoAxisGimbalSolver { if (!control_enabled_) return {nan_, nan_}; - control_direction_ = fast_tf::cast(yaw_link_to_pitch_link(control_direction_yaw_link, pitch), *tf_); return calculate_control_errors(control_direction_yaw_link, pitch); @@ -209,9 +202,6 @@ class TwoAxisGimbalSolver { double x_projected = std::sqrt(x * x + y * y); result.pitch_angle_error = -std::atan2(z * c - x_projected * s, z * s + x_projected * c); - // LOG_INFO("x: %f, y: %f, z: %f, yaw err: %f, pitch err: %f",x, y, - // z,result.yaw_angle_error,result.pitch_angle_error); - return result; } @@ -230,4 +220,4 @@ class TwoAxisGimbalSolver { OdomImu::DirectionVector control_direction_; }; -} // namespace rmcs_core::controller::gimbal \ No newline at end of file +} // namespace rmcs_core::controller::gimbal From e89679b6dd95b6fbbc2adfbc67b03f4b0b436750 Mon Sep 17 00:00:00 2001 From: Embers-of-the-Fire Date: Sat, 14 Feb 2026 19:30:00 +0800 Subject: [PATCH 4/4] fix: fix gimbal xz transform --- .../gimbal/two_axis_gimbal_solver.hpp | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp index a16aa2238..a8f3e7d82 100644 --- a/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp +++ b/rmcs_ws/src/rmcs_core/src/controller/gimbal/two_axis_gimbal_solver.hpp @@ -173,21 +173,17 @@ class TwoAxisGimbalSolver { else if (z < lower_limit_.y()) *control_direction << lower_limit_.x() * projection, lower_limit_.y(); - const auto& [x_,y_,z_] = *control_direction; - - Eigen::Vector2d yaw_projection{x_,y_}; - double yaw_norm = yaw_projection.norm(); - if (yaw_norm > 0) - yaw_projection /=yaw_norm; - else{ - control_enabled_ =false; - return; - } + const auto& [yaw_x, yaw_y, yaw_z] = *control_direction; + + Eigen::Vector2d xz_projection{yaw_x, yaw_z}; + double xz_norm = xz_projection.norm(); + if (xz_norm > 0) + xz_projection /= xz_norm; - if (y_ > yaw_upper_limit_.y()) - *control_direction << yaw_upper_limit_.x() * yaw_projection.x(), yaw_upper_limit_.y(), z_; - else if (y_ < yaw_lower_limit_.y()) - *control_direction << yaw_lower_limit_.x() * yaw_projection.x(), yaw_lower_limit_.y(), z_; + if (yaw_y > yaw_upper_limit_.y()) + *control_direction << yaw_upper_limit_.x() * xz_projection.x(), yaw_upper_limit_.x(), yaw_upper_limit_.x() * xz_projection.y(); + else if (yaw_y < yaw_lower_limit_.y()) + *control_direction << yaw_lower_limit_.x() * xz_projection.x(), yaw_lower_limit_.x(), yaw_lower_limit_.x() * xz_projection.y(); }