diff --git a/README.md b/README.md index 070dc8ad1a1200ba674b91ddf6d4b58e9c39f2c4..38651e571f134b21a7c2602e687d5e6e041f1131 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,99 @@ # PB_RM_Simulation 深圳北理莫斯科大学 北极熊战队 哨兵导航仿真包 -## 1. 项目介绍 -本项目使用麦克纳姆轮仿真小车,附加 Livox mid360 雷达与 IMU,在 2023 RMUC 地图进行导航算法仿真。 +## 一. 项目介绍 +本项目使用麦克纳姆轮仿真小车,附加 Livox Mid360 雷达与 Imu,在 2023 RMUC/RMUL 地图进行导航算法仿真。 -仿真小车:基于 [华农 Taurus 哨兵定位导航算法](https://github.com/SCAU-RM-NAV/rm2023_auto_sentry_ws) 的 simple_meca_car 修改,将其升级为 ROS2 仿真包 - -mid360 点云仿真:参考了 [livox_laser_simulation](https://github.com/Livox-SDK/livox_laser_simulation/blob/main/src/livox_points_plugin.cpp)、 [livox_laser_simulation_RO2](https://github.com/stm32f303ret6/livox_laser_simulation_RO2/blob/main/src/livox_points_plugin.cpp)、 [Issue15: CustomMsg](https://github.com/Livox-SDK/livox_laser_simulation/issues/15) +Mid360 点云仿真:参考了 [livox_laser_simulation](https://github.com/Livox-SDK/livox_laser_simulation/blob/main/src/livox_points_plugin.cpp)、 [livox_laser_simulation_RO2](https://github.com/stm32f303ret6/livox_laser_simulation_RO2/blob/main/src/livox_points_plugin.cpp)、 [Issue15: CustomMsg](https://github.com/Livox-SDK/livox_laser_simulation/issues/15) 导航算法框架:基于 [中南大学 FYT 战队 RM 哨兵上位机算法 ](https://github.com/baiyeweiguang/CSU-RM-Sentry) 略微修改以适配仿真。 非常非常感谢以上开源项目的帮助!!! +|Gazebo 仿真|Fast_LIO + Navigation2| +|:-:|:-:| +||| -![Gazebo_Simulation](docs/Gazebo_Simulation.png) -![FAST_LIO+Nav2](docs/FAST_LIO+Nav2.png) +|Mid360点云仿真 + FAST_LIO 里程计 + 障碍物/地面点云分割 + 三维点云转二维 + Navigation2| +|:-:| +|| -## 2. rm_simulation 话题接口 +## 二. rm_simulation 话题接口 | **Topic name** | **Type** | **Note** | |:-------------------:|:-------------------------------:|:--------------------------------:| -| /mid360 | livox_ros_driver2/msg/CustomMsg | Mid360 自定义消息类型,用于 FAST_LIO 等算法 | -| /mid360_PointCloud2 | sensor_msgs/msg/PointCloud2 | ROS2 通用点云格式 | -| /imu | sensor_msgs/msg/Imu | Gazebo 插件仿真 IMU | -| /cmd_vel | geometry_msgs/msg/Twist | 麦克纳姆轮小车运动控制话题接口 | -| /odom | nav_msgs/msg/Odometry | Gazebo mecanum_controller 输出的里程计 | - - -## 3. 环境配置 -当前开发分支为 ROS2 humble, Gazebo 11.10.0 - -``` -sudo apt-get install ros-humble-gazebo-* -sudo apt-get install ros-humble-xacro -sudo apt-get install ros-humble-robot-state-publisher -sudo apt-get install ros-humble-joint-state-publisher -sudo apt-get install ros-humble-rviz2 -sudo apt-get install libboost-all-dev -``` - -``` -sudo apt install -y ros-humble-nav2* -sudo apt install -y ros-humble-pcl-ros -sudo apt install -y ros-humble-pcl-conversions -sudo apt install -y ros-humble-libpointmatcher -sudo apt install -y ros-humble-tf2-geometry-msgs -sudo apt install -y libgoogle-glog-dev -``` -后续编译与运行过程中如果有依赖功能包未安装,按类似的方法安装。 +| /livox/lidar | livox_ros_driver2/msg/CustomMsg | Mid360 自定义消息类型 | +| /livox/lidar_PointCloud2 | sensor_msgs/msg/PointCloud2 | ROS2 点云消息类型 | +| /livox/imu | sensor_msgs/msg/Imu | Gazebo 插件仿真 IMU | +| /cmd_vel | geometry_msgs/msg/Twist | 麦克纳姆轮小车运动控制接口 | +| /odom | nav_msgs/msg/Odometry | FAST_LIO 输出的里程计 | + + +## 三. 环境配置 +当前开发环境为 Ubuntu22.04, ROS2 humble, Gazebo 11.10.0 + +1. 安装依赖 + + ```bash + sudo apt install -y ros-humble-gazebo-* + sudo apt install -y ros-humble-xacro + sudo apt install -y ros-humble-robot-state-publisher + sudo apt install -y ros-humble-joint-state-publisher + sudo apt install -y ros-humble-rviz2 + sudo apt install -y ros-humble-nav2* + sudo apt install -y ros-humble-pcl-ros + sudo apt install -y ros-humble-pcl-conversions + sudo apt install -y ros-humble-libpointmatcher + sudo apt install -y ros-humble-tf2-geometry-msgs + sudo apt install -y libboost-all-dev + sudo apt install -y libgoogle-glog-dev + ``` + + 后续编译与运行过程中如果有依赖功能包未安装,按类似的方法安装。 + +2. 安装 [Livox SDK2](https://github.com/Livox-SDK/Livox-SDK2) + + ```bash + sudo apt install cmake + ``` + + ```bash + git clone https://github.com/Livox-SDK/Livox-SDK2.git + cd ./Livox-SDK2/ + mkdir build + cd build + cmake .. && make -j + sudo make install + ``` +3. 安装 [Livox ROS Driver 2](https://github.com/Livox-SDK/Livox-SDK) + + ``` + git clone https://github.com/Livox-SDK/livox_ros_driver2.git ws_livox/src/livox_ros_driver2 + ``` + + ``` + cd ws_livox/src/livox_ros_driver2 + source /opt/ros/humble/setup.sh + ./build.sh humble + ``` + + 设置每次启动终端时,自动 source livox_ros_driver2 的工作空间,才能使用 livox 雷达的自定义消息类型。**注意替换 `WHERE_YOU_GIT_CLONE`** + ``` + echo '# livox_ros_driver2' >> ~/.bashrc + echo 'source source /WHERE_YOU_GIT_CLONE/ws_livox/install/setup.bash' >> ~/.bashrc + ``` + +## 四. 编译运行 ``` git clone https://gitee.com/SMBU-POLARBEAR/pb_rmsimulation ``` -## 4. 编译与运行 - -请注意,编译本项目前,需要先 source livox_ros_driver2 软件包的工作空间,详见 [FAST_LIO](https://github.com/Ericsii/FAST_LIO#13-livox_ros_driver2) 的编译教程 - #### 边建图边导航 ``` ./mapping.sh ``` -#### 边建图边导航 +#### 已知全局地图导航 ``` ./nav.sh ``` @@ -73,11 +105,11 @@ source install/setup.bash ros2 run teleop_twist_keyboard teleop_twist_keyboard ``` -## 5. 地图选择 +## 五. 地图选择 | 地图 | 描述 | 来源 | |:---------------------:|:----------:|:---:| -| auto_world.world | 2022 RMUL-步兵对抗 地图 | [华农](https://github.com/SCAU-RM-NAV/rm2022_auto_infantry_ws) | +| auto_world | 2022 RMUL-步兵对抗 地图 | [华农](https://github.com/SCAU-RM-NAV/rm2022_auto_infantry_ws) | | RMUC2023_world | 2023 RMUC 地图 | [Robomaster 官方](https://bbs.robomaster.com/thread-22576-1-1.html) | 当前默认地图为 `RMUC2023_world`
@@ -88,21 +120,35 @@ ros2 run teleop_twist_keyboard teleop_twist_keyboard `ros2 launch pb_rm_simulation rm_simulation.launch.py world:=RMUC`
or
`ros2 launch pb_rm_simulation rm_simulation.launch.py world:=RMUL` -## 6. 雷达选择 -目前支持 livox 系列部分雷达: -avia, HAP, horizon, mid40, mid70, mid360, tele 雷达。 +## 六. 雷达选择 +目前支持 Livox 系列雷达: +Avia, HAP, Horizon, Mid40, Mid70, Mid360, Tele -当前默认使用 mid360 仿真,如需更换其它 Livox 系列雷达进行仿真,请在 [waking_robot.xacro](/src/rm_simulation/pb_rm_simulation/urdf/waking_robot.xacro) 中仿照下述样式修改 +当前默认使用 Mid360 仿真,如需更换其它 Livox 系列雷达进行仿真,请在 [waking_robot.xacro](/src/rm_simulation/pb_rm_simulation/urdf/waking_robot.xacro) 中仿照下述样式修改 ```xml - - + + ``` -## 7. TODO +## ATTENTION 当前项目存在性能问题 +1. 仿真性能瓶颈,livox_laser_simulation 无法多核并行计算,导致一核有难,十核围观。在 RMUC 地图中 mapping 模式 的 /livox/lidar 的频率仅 3.6~4.1Hz,在 nav 模式中频率更低。 + +2. 2023.12.8 版本往后修复了之前错误的 tf 树,现在的里程计完全由 FAST_LIO 提供,在 RMUC 地图中 mapping 模式 的 /Odometry 的频率仅约 3.8Hz ,远远不及使用 Gazebo libgazebo_ros_planar_move 直接提供的 里程计(50~60Hz)),这导致 navigation 控制器发出的小车运动速度很小。 + +3. 在 nav 仿真模式下,运行 icp_localization 时存在概率性中断,抛出以下报错: + ```bash + [icp_localization-1] terminate called after throwing an instance of 'std::runtime_error' + [icp_localization-1] what(): val: 621356012573094357 is not ge than: 621356012573096939 + [ERROR] [icp_localization-1]: process has died [pid 48771, exit code -6, cmd '/home/CLH_NAVIGATION_WS/pb_rmsimulation/install/icp_localization_ros2/lib/icp_localization_ros2/icp_localization --ros-args --params-file /home/CLH_NAVIGATION_WS/pb_rmsimulation/install/icp_localization_ros2/share/icp_localization_ros2/config/node_params.yaml --params-file /tmp/launch_params_rtjf6lwa']. + ``` + 在多台主机上的测试表明,这可能与主机性能有关,尤其是内存大小。 + 内存为 32GB 的 NUC 中断概率较小,而在 16GB 的拯救者在 WSL2 下仅运行 5 秒即中断。 + +## TODO 1. ~~将湖工大地图模型文件修改为动态路径~~(2023.9.30 完成) 2. ~~多线雷达~~(2023.10.8 完成) @@ -114,3 +160,5 @@ avia, HAP, horizon, mid40, mid70, mid360, tele 雷达。 5. ~~优雅的选择地图,并根据不同地图适配小车出生点~~(2023.11.25完成) 6. 决策树 + +7. 优化 livox_laser_simulation 性能问题 diff --git a/docs/RMUL_mapping.gif b/docs/RMUL_mapping.gif new file mode 100644 index 0000000000000000000000000000000000000000..b8d9251080fd145bba058464dc9143259fe506c5 Binary files /dev/null and b/docs/RMUL_mapping.gif differ diff --git a/mapping.sh b/mapping.sh index 3f2293b112be26f7d5e9934123f0aba6455764e1..6d1be23d088dc2161ffd21800568edfa44dfda51 100755 --- a/mapping.sh +++ b/mapping.sh @@ -1,10 +1,28 @@ colcon build --symlink-install -cmds=( "ros2 launch pb_rm_simulation rm_simulation.launch.py world:=RMUC" - "ros2 launch linefit_ground_segmentation_ros segmentation.launch.py" - "ros2 launch fast_lio mapping_mid360.launch.py rviz:=false" - "ros2 launch pointcloud_to_laserscan pointcloud_to_laserscan_launch.py" - "ros2 launch rm_navigation online_async_launch.py" - "ros2 launch rm_navigation bringup_no_amcl_launch.py") + +cmds=( + "ros2 launch pb_rm_simulation rm_simulation.launch.py world:=RMUC \ + use_sim_time:=true + " + "ros2 launch imu_complementary_filter complementary_filter.launch.py \ + use_sim_time:=true + " + "ros2 launch fast_lio mapping_mid360.launch.py \ + use_sim_time:=true rviz:=false + " + "ros2 launch linefit_ground_segmentation_ros segmentation.launch.py \ + use_sim_time:=true + " + "ros2 launch pointcloud_to_laserscan pointcloud_to_laserscan_launch.py \ + use_sim_time:=true + " + "ros2 launch rm_navigation online_async_launch.py \ + use_sim_time:=true + " + "ros2 launch rm_navigation bringup_no_amcl_launch.py \ + use_sim_time:=true + " +) for cmd in "${cmds[@]}" do diff --git a/nav.sh b/nav.sh index db657ffa737f9e0fc36e458bbd1cd5e390780bda..5614d1551b268b1a0279532666b457490b99298d 100755 --- a/nav.sh +++ b/nav.sh @@ -1,12 +1,30 @@ colcon build --symlink-install -cmds=( "ros2 launch pb_rm_simulation rm_simulation.launch.py world:=RMUC" - "ros2 launch linefit_ground_segmentation_ros segmentation.launch.py" - "ros2 launch fast_lio mapping_mid360.launch.py rviz:=false" - "ros2 launch pointcloud_to_laserscan pointcloud_to_laserscan_launch.py" - "ros2 launch icp_localization_ros2 bringup.launch.py" - "ros2 launch rm_navigation bringup_launch.py") -for cmd in "${cmds[@]}"; +cmds=( + "ros2 launch pb_rm_simulation rm_simulation.launch.py world:=RMUC \ + use_sim_time:=true + " + "ros2 launch imu_complementary_filter complementary_filter.launch.py \ + use_sim_time:=true + " + "ros2 launch fast_lio mapping_mid360.launch.py \ + use_sim_time:=true rviz:=false + " + "ros2 launch linefit_ground_segmentation_ros segmentation.launch.py \ + use_sim_time:=true + " + "ros2 launch pointcloud_to_laserscan pointcloud_to_laserscan_launch.py \ + use_sim_time:=true + " + "ros2 launch icp_localization_ros2 bringup.launch.py \ + use_sim_time:=true + " + "ros2 launch rm_navigation bringup_launch.py \ + use_sim_time:=true + " +) + +for cmd in "${cmds[@]}" do echo Current CMD : "$cmd" gnome-terminal -- bash -c "cd $(pwd);source install/setup.bash;$cmd;exec bash;" diff --git a/src/rm_decision/README.md b/src/rm_decision/README.md index b80f923e5d3c79eeb9061510bbd6170c707a42ab..659a6591aaa0d4a14303ffd0042566c858978b1e 100644 --- a/src/rm_decision/README.md +++ b/src/rm_decision/README.md @@ -77,8 +77,6 @@ ros2 launch rm_decision pb_auto_fsm_launch.py ros2 topic pub -r 5 /robot_status rm_decision_interfaces/msg/RobotStatus "{ robot_id: 7, current_hp: 200, - maximum_hp: 200, - shooter_heat_limit: 50, shooter_heat: 19, }" ``` @@ -138,7 +136,12 @@ ros2 launch rm_decision pb_auto_fsm_launch.py orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0} } }" - ``` - +- RFID + ``` + source install/setup.bash + ros2 topic pub -r 5 /rfid rm_decision_interfaces/msg/RFID "{ + rfid_patrol_status: 0 + }" + ``` diff --git a/src/rm_decision/rm_decision/callback_msg.py b/src/rm_decision/rm_decision/callback_msg.py index 3a8a574d64d7f026ac0cdea1511829635bed79f8..6a328e11542675e3386ba6420440b5ff192e96d7 100644 --- a/src/rm_decision/rm_decision/callback_msg.py +++ b/src/rm_decision/rm_decision/callback_msg.py @@ -1,6 +1,12 @@ from rclpy.node import Node from rclpy import logging +from enum import Enum + +class TeamColor(Enum): + RED = 0 + BLUE = 1 + class CallbackMsg(Node): """ 自定义消息回调类 @@ -10,28 +16,32 @@ class CallbackMsg(Node): self.friend_color = 0 # 0-Red, 1-Blue # Robot HP (1-英雄 2-工程 3-步兵 4-步兵 5-步兵 7-哨兵 8-前哨站 9-基地) - self.friend_robot_HP = [0] * 10; - self.enemy_robot_HP = [0] * 10; + self.friend_robot_HP = [0] * 10 + self.enemy_robot_HP = [0] * 10 # Robot Status - self.robot_id = 0 # 机器人ID - self.current_hp = 0 # 当前血量 - self.maximum_hp = 0 # 最大血量 - self.shooter_heat_limit = 0 # 热量上限 - self.shooter_heat = 0 # 发射机构的枪口热量 + self.maximum_hp = 400 # 最大血量 + self.shooter_heat_limit = 400 # 热量上限 + + self.robot_id = None # 机器人ID + self.current_hp = None # 当前血量 + self.shooter_heat = None # 发射机构的枪口热量 # Friend Position self.friend_position = [[]] # RM Vision - self.enemy_dis = 0 + self.enemy_dis = None + + # RFID + self.rfid_patrol_status = None # Custom Variables - self.percent_hp = 0 - self.high_heat_flag = 0 + self.percent_hp = None + self.high_heat_flag = None def callback_AllRobotHP(self, msg): - if self.friend_color == 0: + if self.friend_color == TeamColor.RED: self.friend_robot_HP[1] = msg.red_1_robot_hp self.friend_robot_HP[2] = msg.red_2_robot_hp self.friend_robot_HP[3] = msg.red_3_robot_hp @@ -48,7 +58,7 @@ class CallbackMsg(Node): self.enemy_robot_HP[7] = msg.blue_7_robot_hp # 敌方哨兵血量 self.enemy_robot_HP[8] = msg.blue_outpost_hp self.enemy_robot_HP[9] = msg.blue_base_hp - elif self.friend_color == 1: + elif self.friend_color == TeamColor.BLUE: self.friend_robot_HP[1] = msg.blue_1_robot_hp self.friend_robot_HP[2] = msg.blue_2_robot_hp self.friend_robot_HP[3] = msg.blue_3_robot_hp @@ -82,8 +92,6 @@ class CallbackMsg(Node): def callback_RobotStatus(self, msg): self.robot_id = msg.robot_id self.current_hp = msg.current_hp - self.maximum_hp = msg.maximum_hp - self.shooter_heat_limit = msg.shooter_heat_limit self.shooter_heat = msg.shooter_heat self.percent_hp = self.current_hp / self.maximum_hp @@ -92,26 +100,28 @@ class CallbackMsg(Node): self.high_heat_flag = 1 else: self.high_heat_flag = 0 + ''' - 敌我识别 - TODO:根据24赛季新裁判系统协议,更改敌我判断的条件 + 根据哨兵机器人ID,判断我方阵营颜色 ''' if 1 <= self.robot_id <= 7: - self.friend_color = 0 + self.friend_color = TeamColor.RED # logging.get_logger('RobotStatus').info('We are: RED') elif 101 <= self.robot_id <= 107: - self.friend_color = 1 + self.friend_color = TeamColor.BLUE # logging.get_logger('RobotStatus').info('We are: BLUE') else: - logging.get_logger('RobotStatus').warn('Cannot identified Color') + logging.get_logger('RobotStatus').warn('Cannot identify team color') # DEBUG + # logging.get_logger('RobotStatus').info('We are: ' + str(self.friend_color.name)) # logging.get_logger('RobotStatus').info('self.robot_id: ' + str(self.robot_id)) # logging.get_logger('RobotStatus').info('self.current_hp: ' + str(self.current_hp)) - # logging.get_logger('RobotStatus').info('self.maximum_hp: ' + str(self.maximum_hp)) - # logging.get_logger('RobotStatus').info('self.shooter_heat_limit: ' + str(self.shooter_heat_limit)) # logging.get_logger('RobotStatus').info('self.shooter_heat: ' + str(self.shooter_heat)) # logging.get_logger('RobotStatus').info(' ------------------------------------- ') def callback_rm_vision(self, msg): - self.enemy_dis = msg.distance_to_image_center \ No newline at end of file + self.enemy_dis = msg.distance_to_image_center + + def callback_RFID(self, msg): + self.rfid_patrol_status = msg.rfid_patrol_status \ No newline at end of file diff --git a/src/rm_decision/rm_decision/pb_auto_fsm.py b/src/rm_decision/rm_decision/pb_auto_fsm.py index b8fcf5c70f0ab0d1208f21a75f479a8ba4a53f42..51a5890a1707af0f68e327d8ebfd543fbdb05d78 100644 --- a/src/rm_decision/rm_decision/pb_auto_fsm.py +++ b/src/rm_decision/rm_decision/pb_auto_fsm.py @@ -11,7 +11,7 @@ from rclpy.qos import QoSProfile from rclpy import qos from geometry_msgs.msg import Twist, PoseStamped -from rm_decision_interfaces.msg import RobotStatus, FriendLocation, AllRobotHP, Armor +from rm_decision_interfaces.msg import RobotStatus, FriendLocation, AllRobotHP, Armor, RFID from rm_decision.callback_msg import CallbackMsg from rm_decision.robot_navigator import BasicNavigator @@ -39,6 +39,7 @@ class AutoFSM(Node): self.nav_timeout = 10.0 self.interval_time = 1.0 self.follow_robot_id = None + self.out_patrol_time = 0 self.msg_callback = CallbackMsg() self.FSM_state = 2 @@ -59,11 +60,18 @@ class AutoFSM(Node): '/detector/armors', self.msg_callback.callback_rm_vision, qos.qos_profile_sensor_data) + self.RFID_sub = self.create_subscription(RFID, + '/rfid', + self.msg_callback.callback_RFID, + QoSProfile(depth=1)) - # Init function - Utility.yaml_read(self) + # Set rate self.rate = self.create_rate(0.5) self.rate_nav = self.create_rate(100) + self.rate_RFID = self.create_rate(1) + + # Init function + Utility.yaml_read(self) self.init_wait_for_messages() # Connect to Navigation @@ -72,6 +80,8 @@ class AutoFSM(Node): self.navigator.waitUntilNav2Active() self.navigator.get_logger().info('成功连接导航服务') + time.sleep(3) # 停滞三秒,让所有 msg 都被正常接收 + # Create Threads self.thread_FSM_ = threading.Thread(target=self.thread_FSM) self.thread_FSM_.start() @@ -79,16 +89,32 @@ class AutoFSM(Node): self.thread_FSM_state_ = threading.Thread(target=self.thread_FSM_state) self.thread_FSM_state_.start() + self.thread_RFID_ = threading.Thread(target=self.thread_RFID) + self.thread_RFID_.start() + def init_wait_for_messages(self): message_topics = [ (RobotStatus, '/robot_status'), (AllRobotHP, '/robot_hp'), (FriendLocation, '/friend_location'), - (Armor, '/detector/armors') + (Armor, '/detector/armors'), + (RFID, '/rfid') ] for message_type, topic in message_topics: self.get_logger().info(f'等待 {topic} 消息') Utility.wait_for_message(self, message_type, topic) + + def thread_RFID(self): + while rclpy.ok(): + if self.msg_callback.rfid_patrol_status == 0: + # Not detected patrol_RFID, the robot is not in the patrol area, timer counts in seconds + self.out_patrol_time += 1 + self.get_logger().info('out_patrol_time = ' + str(self.out_patrol_time)) + else: + # Detected patrol_RFID, reset the timer + self.get_logger().info('Detected patrol RFID') + self.out_patrol_time = 0 + self.rate_RFID.sleep() def thread_FSM_state(self): while rclpy.ok(): @@ -96,9 +122,9 @@ class AutoFSM(Node): self.rate_nav.sleep() def state_update(self): - if self.msg_callback.percent_hp < self.set_unhealth_point_percent or self.msg_callback.high_heat_flag == 1: + if self.msg_callback.percent_hp < self.set_unhealth_point_percent or self.msg_callback.high_heat_flag == 1 or self.out_patrol_time > 30: ''' - 血量百分比低于设定值 或 枪口热量过高,进入逃跑状态 + 血量百分比低于设定值 或 枪口热量过高 或 哨兵离开巡逻区过长,进入逃跑状态 ''' self.FSM_state = 4 @@ -146,7 +172,7 @@ class AutoFSM(Node): if reach_result == 1: self.get_logger().info("到达目标点 " + str(self.random_list[random_num])) reach_result = 0 - time.sleep(self.interval_time) + self.rate.sleep() self.get_logger().info("退出随机跑点") def mission_follow(self): diff --git a/src/rm_decision_interfaces/CMakeLists.txt b/src/rm_decision_interfaces/CMakeLists.txt index 04275c34c925146b14c87bac8c3ce24f1373be9b..110503b2beb0b8fd0f13018ea7c6c13d3970f51d 100644 --- a/src/rm_decision_interfaces/CMakeLists.txt +++ b/src/rm_decision_interfaces/CMakeLists.txt @@ -18,6 +18,7 @@ rosidl_generate_interfaces(${PROJECT_NAME} "msg/RobotStatus.msg" "msg/FriendLocation.msg" "msg/Armor.msg" + "msg/RFID.msg" DEPENDENCIES std_msgs diff --git a/src/rm_decision_interfaces/msg/AllRobotHP.msg b/src/rm_decision_interfaces/msg/AllRobotHP.msg index ba44966158c41d5c0a4d59aa21a7dc5aec99587c..da08b498dcbe017f477f7accb4f0b59fe942c033 100644 --- a/src/rm_decision_interfaces/msg/AllRobotHP.msg +++ b/src/rm_decision_interfaces/msg/AllRobotHP.msg @@ -1,3 +1,5 @@ +# v1.6 0x0003 机器人血量数据 + uint16 red_1_robot_hp uint16 red_2_robot_hp uint16 red_3_robot_hp diff --git a/src/rm_decision_interfaces/msg/FriendLocation.msg b/src/rm_decision_interfaces/msg/FriendLocation.msg index a672c8acea6c3889b3bd62bcd677ff8b0224e49e..1398497ad6a3f517268d5146834aaae7a4189679 100644 --- a/src/rm_decision_interfaces/msg/FriendLocation.msg +++ b/src/rm_decision_interfaces/msg/FriendLocation.msg @@ -1,3 +1,5 @@ +# v1.6 0x020B 地面机器人位置数据 + float64 hero_x float64 hero_y diff --git a/src/rm_decision_interfaces/msg/RFID.msg b/src/rm_decision_interfaces/msg/RFID.msg new file mode 100644 index 0000000000000000000000000000000000000000..6b13aed58db2d856f7d3c8acf35ac77a5ffa7ca1 --- /dev/null +++ b/src/rm_decision_interfaces/msg/RFID.msg @@ -0,0 +1,2 @@ +# v1.6 0x0209 机器人RFID状态(此处仅保留我方哨兵巡逻区) +bool rfid_patrol_status \ No newline at end of file diff --git a/src/rm_decision_interfaces/msg/RobotStatus.msg b/src/rm_decision_interfaces/msg/RobotStatus.msg index f42442bf71a8a06fa14de8a7740f5f6e1d35f28a..851fc865c3b41a442dc923306a7805bb1305d0cd 100644 --- a/src/rm_decision_interfaces/msg/RobotStatus.msg +++ b/src/rm_decision_interfaces/msg/RobotStatus.msg @@ -1,8 +1,6 @@ -# From 0x0201 +# v1.6 0x0201 机器人性能体系数据 uint8 robot_id # 机器人ID (1~7->红,101~107->蓝) -uint16 current_hp # 当前血量 -uint16 maximum_hp # 最大血量 -uint16 shooter_heat_limit # 热量上限 +uint16 current_hp # 血量 -# From 0x0202 -uint16 shooter_heat # 发射机构的枪口热量 +# v1.6 0x0202 实时功率热量数据 +uint16 shooter_heat # 枪口热量 diff --git a/src/rm_localization/FAST_LIO/config/mid360.yaml b/src/rm_localization/FAST_LIO/config/mid360.yaml index cc5760d3345c36b0a165c6121656b4133235f490..bee5a839e6c9ac27ea4090f54cc5a327dd580be8 100644 --- a/src/rm_localization/FAST_LIO/config/mid360.yaml +++ b/src/rm_localization/FAST_LIO/config/mid360.yaml @@ -1,8 +1,8 @@ /**: ros__parameters: common: - lid_topic: "/mid360" - imu_topic: "/imu" + lid_topic: "/livox/lidar" + imu_topic: "/imu/data" time_sync_en: false # ONLY turn on when external time synchronization is really not possible time_offset_lidar_to_imu: 0.0 # Time offset between lidar and IMU calibrated by other algorithms, e.g. LI-Init (can be found in README). # This param will take effect no matter what time_sync_en is. So if the time offset is not known exactly, please set as 0.0 @@ -20,14 +20,13 @@ b_acc_cov: 0.0001 b_gyr_cov: 0.0001 fov_degree: 360.0 - det_range: 200.0 + det_range: 100.0 extrinsic_est_en: true # true: enable the online estimation of IMU-LiDAR extrinsic, extrinsic_T: [ 0.00, 0.00, -0.05 ] extrinsic_R: [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] - publish: path_en: true # true: publish Path scan_publish_en: true # false: close all the point cloud output diff --git a/src/rm_localization/FAST_LIO/rviz_cfg/fastlio.rviz b/src/rm_localization/FAST_LIO/rviz_cfg/fastlio.rviz index 5550b795307984157d7c483b37caa953e2a154ca..467dd5c4342936d84685c013c6c276a8d6ceece0 100644 --- a/src/rm_localization/FAST_LIO/rviz_cfg/fastlio.rviz +++ b/src/rm_localization/FAST_LIO/rviz_cfg/fastlio.rviz @@ -26,7 +26,7 @@ Panels: Experimental: false Name: Time SyncMode: 0 - SyncSource: CloudRegistered + SyncSource: CloudMap Visualization Manager: Class: "" Displays: @@ -35,9 +35,9 @@ Visualization Manager: Frame Timeout: 15 Frames: All Enabled: true - body: + livox_frame: Value: true - camera_init: + odom: Value: true Marker Scale: 1 Name: TF @@ -45,8 +45,8 @@ Visualization Manager: Show Axes: true Show Names: false Tree: - camera_init: - body: + odom: + livox_frame: {} Update Interval: 0 Value: true @@ -188,8 +188,8 @@ Visualization Manager: - Alpha: 1 Autocompute Intensity Bounds: true Autocompute Value Bounds: - Max Value: 4.614229202270508 - Min Value: -1.0074758529663086 + Max Value: 2.9017436504364014 + Min Value: -2.2115612030029297 Value: true Axis: Z Channel Name: intensity @@ -222,7 +222,7 @@ Visualization Manager: Enabled: true Global Options: Background Color: 0; 0; 0 - Fixed Frame: camera_init + Fixed Frame: odom Frame Rate: 30 Name: root Tools: @@ -280,10 +280,10 @@ Visualization Manager: Invert Z Axis: false Name: Current View Near Clip Distance: 0.009999999776482582 - Pitch: 0.454797625541687 + Pitch: 0.9397971630096436 Target Frame: Value: Orbit (rviz_default_plugins) - Yaw: 4.193554878234863 + Yaw: 4.408558368682861 Saved: ~ Window Geometry: Displays: diff --git a/src/rm_localization/FAST_LIO/src/laserMapping.cpp b/src/rm_localization/FAST_LIO/src/laserMapping.cpp index d4585ec14aa7fd7b0f02cc2fc94be5f023c1069b..03c1232077c583d22614c21405a9772f86953773 100644 --- a/src/rm_localization/FAST_LIO/src/laserMapping.cpp +++ b/src/rm_localization/FAST_LIO/src/laserMapping.cpp @@ -507,7 +507,7 @@ void publish_frame_world(rclcpp::Publisher::Share pcl::toROSMsg(*laserCloudWorld, laserCloudmsg); // laserCloudmsg.header.stamp = ros::Time().fromSec(lidar_end_time); laserCloudmsg.header.stamp = get_ros_time(lidar_end_time); - laserCloudmsg.header.frame_id = "camera_init"; + laserCloudmsg.header.frame_id = "odom"; pubLaserCloudFull->publish(laserCloudmsg); publish_count -= PUBFRAME_PERIOD; } @@ -559,7 +559,7 @@ void publish_frame_body(rclcpp::Publisher::Shared sensor_msgs::msg::PointCloud2 laserCloudmsg; pcl::toROSMsg(*laserCloudIMUBody, laserCloudmsg); laserCloudmsg.header.stamp = get_ros_time(lidar_end_time); - laserCloudmsg.header.frame_id = "body"; + laserCloudmsg.header.frame_id = "livox_frame"; pubLaserCloudFull_body->publish(laserCloudmsg); publish_count -= PUBFRAME_PERIOD; } @@ -576,7 +576,7 @@ void publish_effect_world(rclcpp::Publisher::Shar sensor_msgs::msg::PointCloud2 laserCloudFullRes3; pcl::toROSMsg(*laserCloudWorld, laserCloudFullRes3); laserCloudFullRes3.header.stamp = get_ros_time(lidar_end_time); - laserCloudFullRes3.header.frame_id = "camera_init"; + laserCloudFullRes3.header.frame_id = "odom"; pubLaserCloudEffect->publish(laserCloudFullRes3); } @@ -597,7 +597,7 @@ void publish_map(rclcpp::Publisher::SharedPtr pub sensor_msgs::msg::PointCloud2 laserCloudmsg; pcl::toROSMsg(*pcl_wait_pub, laserCloudmsg); laserCloudmsg.header.stamp = get_ros_time(lidar_end_time); - laserCloudmsg.header.frame_id = "camera_init"; + laserCloudmsg.header.frame_id = "odom"; pubLaserCloudMap->publish(laserCloudmsg); } @@ -622,8 +622,8 @@ void set_posestamp(T & out) void publish_odometry(const rclcpp::Publisher::SharedPtr pubOdomAftMapped, std::unique_ptr & tf_br) { - odomAftMapped.header.frame_id = "camera_init"; - odomAftMapped.child_frame_id = "body"; + odomAftMapped.header.frame_id = "odom"; + odomAftMapped.child_frame_id = "base_link"; odomAftMapped.header.stamp = get_ros_time(lidar_end_time); set_posestamp(odomAftMapped.pose); pubOdomAftMapped->publish(odomAftMapped); @@ -640,8 +640,9 @@ void publish_odometry(const rclcpp::Publisher::SharedPt } geometry_msgs::msg::TransformStamped trans; - trans.header.frame_id = "camera_init"; - trans.child_frame_id = "body"; + trans.header.frame_id = "odom"; + trans.child_frame_id = "base_link"; + trans.header.stamp = odomAftMapped.header.stamp; //ADD trans.transform.translation.x = odomAftMapped.pose.pose.position.x; trans.transform.translation.y = odomAftMapped.pose.pose.position.y; trans.transform.translation.z = odomAftMapped.pose.pose.position.z; @@ -656,7 +657,7 @@ void publish_path(rclcpp::Publisher::SharedPtr pubPath) { set_posestamp(msg_body_pose); msg_body_pose.header.stamp = get_ros_time(lidar_end_time); // ros::Time().fromSec(lidar_end_time); - msg_body_pose.header.frame_id = "camera_init"; + msg_body_pose.header.frame_id = "odom"; /*** if path is too large, the rvis will crash ***/ static int jjj = 0; @@ -893,7 +894,7 @@ public: RCLCPP_INFO(this->get_logger(), "p_pre->lidar_type %d", p_pre->lidar_type); path.header.stamp = this->get_clock()->now(); - path.header.frame_id ="camera_init"; + path.header.frame_id ="odom"; FOV_DEG = (fov_deg + 10.0) > 179.9 ? 179.9 : (fov_deg + 10.0); HALF_FOV_COS = cos((FOV_DEG) * 0.5 * PI_M / 180.0); diff --git a/src/rm_localization/icp_localization_ros2/config/node_params.yaml b/src/rm_localization/icp_localization_ros2/config/node_params.yaml index 6a4703800cc9442a8db2d917afa0a106e93ae28a..ae1e2c3ad0fc4d0b1af2ab512152144ae2c4af97 100644 --- a/src/rm_localization/icp_localization_ros2/config/node_params.yaml +++ b/src/rm_localization/icp_localization_ros2/config/node_params.yaml @@ -4,11 +4,11 @@ icp_localization_ros2: range_data_topic: "/cloud_registered_body" num_accumulated_range_data: 1 - imu_data_topic: "/imu" - odometry_data_topic: "/odom" + imu_data_topic: "/imu/data" + odometry_data_topic: "/Odometry" # Utilize '/Odometry' from FAST_LIO when in REAL environment is_use_odometry: true - is_provide_odom_frame: false - gravity_vector_filter_time_constant: 0.05 + is_provide_odom_frame: true + gravity_vector_filter_time_constant: 0.01 fixed_frame: "map" min_num_odom_msgs_before_ready: 10 @@ -30,8 +30,8 @@ yaw: 0.0 odometry_source_to_range_sensor: - x: 0.000 - y: 0.000 + x: 0.0 + y: 0.0 z: 0.0 roll: 0.0 pitch: 0.0 diff --git a/src/rm_navigation/params/mapper_params_online_async.yaml b/src/rm_navigation/params/mapper_params_online_async.yaml index 0c63d22ac5f4b820ea1ee9d2e8caf26a56067966..f1677b6d2dfd12b30b98c09e38e353efe5e595d3 100644 --- a/src/rm_navigation/params/mapper_params_online_async.yaml +++ b/src/rm_navigation/params/mapper_params_online_async.yaml @@ -12,7 +12,7 @@ slam_toolbox: # ROS Parameters odom_frame: odom map_frame: map - base_frame: livox + base_frame: livox_frame scan_topic: /scan mode: mapping #localization @@ -70,4 +70,4 @@ slam_toolbox: coarse_angle_resolution: 0.0349 minimum_angle_penalty: 0.9 minimum_distance_penalty: 0.5 - use_response_expansion: true + use_response_expansion: true \ No newline at end of file diff --git a/src/rm_navigation/params/nav2_params.yaml b/src/rm_navigation/params/nav2_params.yaml index 280aa96d605e61ce761376cba14b05b0dc56fef1..2630e4adefc4dae9eb0dca2c48e3a062331cf5a3 100644 --- a/src/rm_navigation/params/nav2_params.yaml +++ b/src/rm_navigation/params/nav2_params.yaml @@ -44,7 +44,7 @@ bt_navigator: use_sim_time: True global_frame: map robot_base_frame: base_link - odom_topic: /odom + odom_topic: /Odometry bt_loop_duration: 10 default_server_timeout: 20 # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults: @@ -136,86 +136,84 @@ controller_server: plugin: "nav2_controller::SimpleGoalChecker" xy_goal_tolerance: 0.25 yaw_goal_tolerance: 0.8 - # PurePursuit + # PurePursuit + # https://navigation.ros.org/configuration/packages/configuring-regulated-pp.html FollowPath: plugin: "nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController" - # https://navigation.ros.org/configuration/packages/configuring-regulated-pp.html desired_linear_vel: 2.5 lookahead_dist: 0.6 min_lookahead_dist: 0.3 max_lookahead_dist: 0.9 lookahead_time: 1.5 - rotate_to_heading_angular_vel: 20.0 + rotate_to_heading_angular_vel: 10.0 transform_tolerance: 0.1 - use_velocity_scaled_lookahead_dist: false + use_velocity_scaled_lookahead_dist: true min_approach_linear_velocity: 0.05 - approach_velocity_scaling_dist: 0.6 - use_collision_detection: true + approach_velocity_scaling_dist: 2.0 + use_collision_detection: false max_allowed_time_to_collision_up_to_carrot: 1.0 use_regulated_linear_velocity_scaling: true use_fixed_curvature_lookahead: false - curvature_lookahead_dist: 0.6 - use_cost_regulated_linear_velocity_scaling: false - regulated_linear_scaling_min_radius: 0.25 + curvature_lookahead_dist: 2.0 + use_cost_regulated_linear_velocity_scaling: true + regulated_linear_scaling_min_radius: 0.15 regulated_linear_scaling_min_speed: 0.5 use_rotate_to_heading: true allow_reversing: false rotate_to_heading_min_angle: 0.785 - max_angular_accel: 20.0 + max_angular_accel: 15.0 max_robot_pose_search_dist: 10.0 use_interpolation: true # DWB parameters - # FollowPath: - # plugin: "dwb_core::DWBLocalPlanner" - # debug_trajectory_details: True - # min_vel_x: -3.0 - # min_vel_y: -2.0 - # max_vel_x: 3.0 - # max_vel_y: 2.0 - # max_vel_theta: 10.0 - # min_speed_xy: 0.0 - # max_speed_xy: 3.0 - # min_speed_theta: 0.0 - # # Add high threshold velocity for turtlebot 3 issue. - # # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75 - # acc_lim_x: 2.5 - # acc_lim_y: 2.0 - # acc_lim_theta: 5.0 - # decel_lim_x: -2.5 - # decel_lim_y: -2.0 - # decel_lim_theta: -3.2 - # vx_samples: 20 - # vy_samples: 5 - # vtheta_samples: 20 - # sim_time: 1.7 - # linear_granularity: 0.05 - # angular_granularity: 0.025 - # transform_tolerance: 0.2 - # xy_goal_tolerance: 0.25 - # trans_stopped_velocity: 0.25 - # short_circuit_trajectory_evaluation: True - # stateful: True - # critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"] - # BaseObstacle.scale: 0.02 - # PathAlign.scale: 32.0 - # PathAlign.forward_point_distance: 0.1 - # GoalAlign.scale: 24.0 - # GoalAlign.forward_point_distance: 0.1 - # PathDist.scale: 32.0 - # GoalDist.scale: 24.0 - # RotateToGoal.scale: 32.0 - # RotateToGoal.slowing_factor: 5.0 - # RotateToGoal.lookahead_time: -1.0 + FollowPath: + plugin: "dwb_core::DWBLocalPlanner" + debug_trajectory_details: True + min_vel_x: -2.5 + min_vel_y: -2.5 + max_vel_x: 2.5 + max_vel_y: 2.5 + max_vel_theta: 12.0 + min_speed_xy: -8.0 + max_speed_xy: 8.0 + min_speed_theta: -12.0 + acc_lim_x: 5.0 + acc_lim_y: 5.0 + acc_lim_theta: 15.0 + decel_lim_x: -5.0 + decel_lim_y: -5.0 + decel_lim_theta: -15.0 + vx_samples: 20 + vy_samples: 20 + vtheta_samples: 20 + sim_time: 0.16 + linear_granularity: 0.05 + angular_granularity: 0.025 + transform_tolerance: 0.2 + xy_goal_tolerance: 0.25 + trans_stopped_velocity: 0.25 + short_circuit_trajectory_evaluation: True + stateful: True + critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"] + BaseObstacle.scale: 0.02 + PathAlign.scale: 32.0 + GoalAlign.scale: 24.0 + PathAlign.forward_point_distance: 0.1 + GoalAlign.forward_point_distance: 0.1 + PathDist.scale: 32.0 + GoalDist.scale: 24.0 + RotateToGoal.scale: 32.0 + RotateToGoal.slowing_factor: 5.0 + RotateToGoal.lookahead_time: -1.0 # Recovery - shrink_horizon_backup: True - shrink_horizon_min_duration: 10.0 - oscillation_recovery: True - oscillation_v_eps: 0.1 - oscillation_omega_eps: 0.1 - oscillation_recovery_mimaxn_duration: 10.0 - oscillation_filter_duration: 10.0 + # shrink_horizon_backup: True + # shrink_horizon_min_duration: 10.0 + # oscillation_recovery: True + # oscillation_v_eps: 0.1 + # oscillation_omega_eps: 0.1 + # oscillation_recovery_mimaxn_duration: 10.0 + # oscillation_filter_duration: 10.0 local_costmap: @@ -399,11 +397,11 @@ velocity_smoother: smoothing_frequency: 20.0 scale_velocities: True feedback: "OPEN_LOOP" - max_velocity: [2.0, 5.0, 12.0] - min_velocity: [-2.0, -5.0, -12.0] - max_accel: [5.5, 3.0, 15.2] - max_decel: [-5.5, -3.0, -15.2] - odom_topic: "odom" + max_velocity: [2.5, 2.5, 12.0] + min_velocity: [-2.5, -2.5, -12.0] + max_accel: [5.0, 5.0, 15.0] + max_decel: [-5.0, -5.0, -15.0] + odom_topic: "Odometry" odom_duration: 0.1 deadband_velocity: [0.0, 0.0, 0.0] velocity_timeout: 1.0 diff --git a/src/rm_perception/imu_complementary_filter/CHANGELOG.rst b/src/rm_perception/imu_complementary_filter/CHANGELOG.rst new file mode 100644 index 0000000000000000000000000000000000000000..d2aede662b696c7f6e1c6edda5765856db49e799 --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/CHANGELOG.rst @@ -0,0 +1,128 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package imu_complementary_filter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +2.1.3 (2022-12-07) +------------------ +* complementary: Build shared library + See `#172 `_. +* Update CMakeLists to use targets +* Remove node\_ prefix. (`#163 `_) +* Contributors: Martin Günther, Max Polzin + +2.1.2 (2022-07-14) +------------------ + +2.1.1 (2022-05-24) +------------------ +* Add missing build dependency to package.xml. (`#161 `_) +* Contributors: Martin Günther, Steven! Ragnarök + +2.1.0 (2022-05-02) +------------------ +* complementary: Add missing dependency on geometry_msgs +* Contributors: Martin Günther + +2.0.0 (2022-04-12) +------------------ +* Initial release into ROS2 foxy, galactic and rolling +* Fix gcc warnings + clang-tidy suggestions +* Fix CMakeLists +* Reformat python code using black +* Manually reformat licenses + defines +* Reformat everything using clang-format +* Fix trailing whitespace +* Add launch directory to CMakeLists.txt (`#146 `_) +* Port imu_complementary_filter to ROS2 (`#138 `_) +* Madgwick for eloquent (`#110 `_) +* Contributors: Guido Sanchez, Martin Günther, Maximilian Schik, tgreier + +1.2.2 (2020-05-25) +------------------ +* fix install path & boost linkage issues +* Contributors: Martin Günther, Sean Yen + +1.2.1 (2019-05-06) +------------------ +* Remove junk xml (`#93 `_) +* Fix C++14 builds (`#89 `_) +* Contributors: David V. Lu!!, Paul Bovbel + +1.2.0 (2018-05-25) +------------------ +* Add std dev parameter to orientation estimate from filter (`#85 `_) + Similar to `#41 `_, but not using dynamic_reconfigure as not implemented for complementary filter +* Contributors: Stefan Kohlbrecher + +1.1.5 (2017-05-24) +------------------ + +1.1.4 (2017-05-22) +------------------ + +1.1.3 (2017-03-10) +------------------ +* complementary_filter: move const initializations out of header + Initialization of static consts other than int (here: float) inside the + class declaration is not permitted in C++. It works in gcc (due to a + non-standard extension), but throws an error in C++11. +* Contributors: Martin Guenther + +1.1.2 (2016-09-07) +------------------ + +1.1.1 (2016-09-07) +------------------ + +1.1.0 (2016-04-25) +------------------ + +1.0.11 (2016-04-22) +------------------- + +1.0.10 (2016-04-22) +------------------- +* Remove Eigen dependency + Eigen is not actually used anywhere. Thanks @asimay! +* Removed main function from shared library +* Contributors: Martin Guenther, Matthias Nieuwenhuisen + +1.0.9 (2015-10-16) +------------------ +* complementary: Add Eigen dependency + Fixes `#54 `_. +* Contributors: Martin Günther + +1.0.8 (2015-10-07) +------------------ + +1.0.7 (2015-10-07) +------------------ +* Allow remapping imu namespace +* Publish RPY as Vector3Stamped +* Add params: constant_dt, publish_tf, reverse_tf, publish_debug_topics +* Use MagneticField instead of Vector3 +* Contributors: Martin Günther + +1.0.6 (2015-10-06) +------------------ +* Add new package: imu_complementary_filter +* Contributors: Roberto G. Valentini, Martin Günther, Michael Görner + +1.0.5 (2015-06-24) +------------------ + +1.0.4 (2015-05-06) +------------------ + +1.0.3 (2015-01-29) +------------------ + +1.0.2 (2015-01-27) +------------------ + +1.0.1 (2014-12-10) +------------------ + +1.0.0 (2014-11-28) +------------------ diff --git a/src/rm_perception/imu_complementary_filter/CMakeLists.txt b/src/rm_perception/imu_complementary_filter/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..79aa917cfc0882cf472516a04c67a81a5997ac9a --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.8) +project(imu_complementary_filter) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() +add_compile_options(-std=c++17 -O3) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(ament_cmake REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(message_filters REQUIRED) +find_package(rclcpp REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(std_msgs REQUIRED) +find_package(tf2 REQUIRED) +find_package(tf2_ros REQUIRED) + +add_library(complementary_filter SHARED + src/complementary_filter.cpp + src/complementary_filter_ros.cpp + include/imu_complementary_filter/complementary_filter.h + include/imu_complementary_filter/complementary_filter_ros.h +) +target_compile_features(complementary_filter PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17 +target_include_directories(complementary_filter PUBLIC + $ + $) +ament_target_dependencies(complementary_filter + geometry_msgs + message_filters + rclcpp + sensor_msgs + std_msgs + tf2 + tf2_ros +) +# Causes the visibility macros to use dllexport rather than dllimport, +# which is appropriate when building the dll but not consuming it. +target_compile_definitions(complementary_filter PRIVATE "IMU_COMPLEMENTARY_FILTER_BUILDING_LIBRARY") + +# create complementary_filter_node executable +add_executable(complementary_filter_node + src/complementary_filter_node.cpp) +target_include_directories(complementary_filter_node PUBLIC + $ + $) +target_link_libraries(complementary_filter_node complementary_filter) + +install( + TARGETS complementary_filter + EXPORT export_${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) +install(TARGETS + complementary_filter_node + DESTINATION lib/${PROJECT_NAME} +) + +## Mark cpp header files for installation +install( + DIRECTORY include/ + DESTINATION include +) + +install(DIRECTORY launch + DESTINATION share/${PROJECT_NAME} +) + +ament_export_include_directories(include) +ament_export_libraries(complementary_filter) +ament_export_targets( + export_${PROJECT_NAME} +) +ament_package() diff --git a/src/rm_perception/imu_complementary_filter/include/imu_complementary_filter/complementary_filter.h b/src/rm_perception/imu_complementary_filter/include/imu_complementary_filter/complementary_filter.h new file mode 100644 index 0000000000000000000000000000000000000000..4758cad2e3a7905a20e368f556e47067640751a0 --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/include/imu_complementary_filter/complementary_filter.h @@ -0,0 +1,175 @@ +/* + @author Roberto G. Valenti + + @section LICENSE + Copyright (c) 2015, City University of New York + CCNY Robotics Lab + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef IMU_TOOLS_COMPLEMENTARY_FILTER_H +#define IMU_TOOLS_COMPLEMENTARY_FILTER_H + +namespace imu_tools { + +class ComplementaryFilter +{ + public: + ComplementaryFilter(); + virtual ~ComplementaryFilter(); + + bool setGainAcc(double gain); + bool setGainMag(double gain); + double getGainAcc() const; + double getGainMag() const; + + bool setBiasAlpha(double bias_alpha); + double getBiasAlpha() const; + + // When the filter is in the steady state, bias estimation will occur (if + // the parameter is enabled). + bool getSteadyState() const; + + void setDoBiasEstimation(bool do_bias_estimation); + bool getDoBiasEstimation() const; + + void setDoAdaptiveGain(bool do_adaptive_gain); + bool getDoAdaptiveGain() const; + + double getAngularVelocityBiasX() const; + double getAngularVelocityBiasY() const; + double getAngularVelocityBiasZ() const; + + // Set the orientation, as a Hamilton Quaternion, of the body frame wrt the + // fixed frame. + void setOrientation(double q0, double q1, double q2, double q3); + + // Get the orientation, as a Hamilton Quaternion, of the body frame wrt the + // fixed frame. + void getOrientation(double& q0, double& q1, double& q2, double& q3) const; + + // Update from accelerometer and gyroscope data. + // [ax, ay, az]: Normalized gravity vector. + // [wx, wy, wz]: Angular veloctiy, in rad / s. + // dt: time delta, in seconds. + void update(double ax, double ay, double az, double wx, double wy, + double wz, double dt); + + // Update from accelerometer, gyroscope, and magnetometer data. + // [ax, ay, az]: Normalized gravity vector. + // [wx, wy, wz]: Angular veloctiy, in rad / s. + // [mx, my, mz]: Magnetic field, units irrelevant. + // dt: time delta, in seconds. + void update(double ax, double ay, double az, double wx, double wy, + double wz, double mx, double my, double mz, double dt); + + private: + static const double kGravity; + static const double gamma_; + // Bias estimation steady state thresholds + static const double kAngularVelocityThreshold; + static const double kAccelerationThreshold; + static const double kDeltaAngularVelocityThreshold; + + // Gain parameter for the complementary filter, belongs in [0, 1]. + double gain_acc_; + double gain_mag_; + + // Bias estimation gain parameter, belongs in [0, 1]. + double bias_alpha_; + + // Parameter whether to do bias estimation or not. + bool do_bias_estimation_; + + // Parameter whether to do adaptive gain or not. + bool do_adaptive_gain_; + + bool initialized_; + bool steady_state_; + + // The orientation as a Hamilton quaternion (q0 is the scalar). Represents + // the orientation of the fixed frame wrt the body frame. + double q0_, q1_, q2_, q3_; + + // Bias in angular velocities; + double wx_prev_, wy_prev_, wz_prev_; + + // Bias in angular velocities; + double wx_bias_, wy_bias_, wz_bias_; + + void updateBiases(double ax, double ay, double az, double wx, double wy, + double wz); + + bool checkState(double ax, double ay, double az, double wx, double wy, + double wz) const; + + void getPrediction(double wx, double wy, double wz, double dt, + double& q0_pred, double& q1_pred, double& q2_pred, + double& q3_pred) const; + + void getMeasurement(double ax, double ay, double az, double& q0_meas, + double& q1_meas, double& q2_meas, double& q3_meas); + + void getMeasurement(double ax, double ay, double az, double mx, double my, + double mz, double& q0_meas, double& q1_meas, + double& q2_meas, double& q3_meas); + + void getAccCorrection(double ax, double ay, double az, double p0, double p1, + double p2, double p3, double& dq0, double& dq1, + double& dq2, double& dq3); + + void getMagCorrection(double mx, double my, double mz, double p0, double p1, + double p2, double p3, double& dq0, double& dq1, + double& dq2, double& dq3); + + double getAdaptiveGain(double alpha, double ax, double ay, double az); +}; + +// Utility math functions: + +void normalizeVector(double& x, double& y, double& z); + +void normalizeQuaternion(double& q0, double& q1, double& q2, double& q3); + +void scaleQuaternion(double gain, double& dq0, double& dq1, double& dq2, + double& dq3); + +void invertQuaternion(double q0, double q1, double q2, double q3, + double& q0_inv, double& q1_inv, double& q2_inv, + double& q3_inv); + +void quaternionMultiplication(double p0, double p1, double p2, double p3, + double q0, double q1, double q2, double q3, + double& r0, double& r1, double& r2, double& r3); + +void rotateVectorByQuaternion(double x, double y, double z, double q0, + double q1, double q2, double q3, double& vx, + double& vy, double& vz); + +} // namespace imu_tools + +#endif // IMU_TOOLS_COMPLEMENTARY_FILTER_H diff --git a/src/rm_perception/imu_complementary_filter/include/imu_complementary_filter/complementary_filter_ros.h b/src/rm_perception/imu_complementary_filter/include/imu_complementary_filter/complementary_filter_ros.h new file mode 100644 index 0000000000000000000000000000000000000000..9c9a44f5a317d8b1c1858a16332e8983a725c503 --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/include/imu_complementary_filter/complementary_filter_ros.h @@ -0,0 +1,107 @@ +/* + @author Roberto G. Valenti + + @section LICENSE + Copyright (c) 2015, City University of New York + CCNY Robotics Lab + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef IMU_TOOLS_COMPLEMENTARY_FILTER_ROS_H +#define IMU_TOOLS_COMPLEMENTARY_FILTER_ROS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imu_complementary_filter/complementary_filter.h" + +namespace imu_tools { + +class ComplementaryFilterROS : public rclcpp::Node +{ + public: + ComplementaryFilterROS(); + ~ComplementaryFilterROS() override; + + private: + // Convenience typedefs + typedef sensor_msgs::msg::Imu ImuMsg; + typedef sensor_msgs::msg::MagneticField MagMsg; + typedef geometry_msgs::msg::Vector3Stamped RpyVectorMsg; + typedef message_filters::sync_policies::ApproximateTime + SyncPolicy; + typedef message_filters::Synchronizer Synchronizer; + typedef message_filters::Subscriber ImuSubscriber; + typedef message_filters::Subscriber MagSubscriber; + + // ROS-related variables. + std::shared_ptr imu_subscriber_; + std::shared_ptr mag_subscriber_; + std::shared_ptr sync_; + + rclcpp::Publisher::SharedPtr imu_publisher_; + rclcpp::Publisher::SharedPtr rpy_publisher_; + rclcpp::Publisher::SharedPtr state_publisher_; + tf2_ros::TransformBroadcaster tf_broadcaster_; + + // Parameters: + bool use_mag_{}; + bool publish_tf_{}; + bool reverse_tf_{}; + double constant_dt_{}; + bool publish_debug_topics_{}; + std::string fixed_frame_; + double orientation_variance_{}; + + // State variables: + ComplementaryFilter filter_; + rclcpp::Time time_prev_; + bool initialized_filter_; + + void initializeParams(); + void imuCallback(ImuMsg::ConstSharedPtr imu_msg_raw); + void imuMagCallback(ImuMsg::ConstSharedPtr imu_msg_raw, + MagMsg::ConstSharedPtr mav_msg); + void publish(ImuMsg::ConstSharedPtr imu_msg_raw); + + tf2::Quaternion hamiltonToTFQuaternion(double q0, double q1, double q2, + double q3) const; +}; + +} // namespace imu_tools + +#endif // IMU_TOOLS_COMPLEMENTARY_FILTER_ROS_H diff --git a/src/rm_perception/imu_complementary_filter/launch/complementary_filter.launch.py b/src/rm_perception/imu_complementary_filter/launch/complementary_filter.launch.py new file mode 100644 index 0000000000000000000000000000000000000000..8a10720998b9ce49308f40083c2b0ea90b555328 --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/launch/complementary_filter.launch.py @@ -0,0 +1,25 @@ +from launch import LaunchDescription +from launch_ros.actions import Node + + +def generate_launch_description(): + return LaunchDescription( + [ + Node( + package='imu_complementary_filter', + executable='complementary_filter_node', + name='complementary_filter_gain_node', + output='screen', + parameters=[ + {'do_bias_estimation': True}, + {'do_adaptive_gain': True}, + {'use_mag': False}, + {'gain_acc': 0.01}, + {'gain_mag': 0.01}, + ], + remappings=[ + ('/imu/data_raw', '/livox/imu'), + ] + ) + ] + ) diff --git a/src/rm_perception/imu_complementary_filter/package.xml b/src/rm_perception/imu_complementary_filter/package.xml new file mode 100644 index 0000000000000000000000000000000000000000..d3c79511fe851f2f2c2cdb0796b19ac8ef91ecaa --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/package.xml @@ -0,0 +1,24 @@ + + + imu_complementary_filter + 2.1.3 + Filter which fuses angular velocities, accelerations, and (optionally) magnetic readings from a generic IMU device into a quaternion to represent the orientation of the device wrt the global frame. Based on the algorithm by Roberto G. Valenti etal. described in the paper "Keeping a Good Attitude: A Quaternion-Based Orientation Filter for IMUs and MARGs" available at http://www.mdpi.com/1424-8220/15/8/19302 . + + Martin Günther + BSD + + http://www.mdpi.com/1424-8220/15/8/19302 + Roberto G. Valenti + + ament_cmake + geometry_msgs + message_filters + rclcpp + sensor_msgs + std_msgs + tf2 + tf2_ros + + ament_cmake + + diff --git a/src/rm_perception/imu_complementary_filter/src/complementary_filter.cpp b/src/rm_perception/imu_complementary_filter/src/complementary_filter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1581ed91fd03416bb58380e60e43659cb36190e6 --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/src/complementary_filter.cpp @@ -0,0 +1,535 @@ +/* + @author Roberto G. Valenti + + @section LICENSE + Copyright (c) 2015, City University of New York + CCNY Robotics Lab + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "imu_complementary_filter/complementary_filter.h" + +#include +#include +#include + +namespace imu_tools { + +const double ComplementaryFilter::kGravity = 9.81; +const double ComplementaryFilter::gamma_ = 0.01; +// Bias estimation steady state thresholds +const double ComplementaryFilter::kAngularVelocityThreshold = 0.2; +const double ComplementaryFilter::kAccelerationThreshold = 0.1; +const double ComplementaryFilter::kDeltaAngularVelocityThreshold = 0.01; + +ComplementaryFilter::ComplementaryFilter() + : gain_acc_(0.01), + gain_mag_(0.01), + bias_alpha_(0.01), + do_bias_estimation_(true), + do_adaptive_gain_(false), + initialized_(false), + steady_state_(false), + q0_(1), + q1_(0), + q2_(0), + q3_(0), + wx_prev_(0), + wy_prev_(0), + wz_prev_(0), + wx_bias_(0), + wy_bias_(0), + wz_bias_(0) +{ +} + +ComplementaryFilter::~ComplementaryFilter() = default; + +void ComplementaryFilter::setDoBiasEstimation(bool do_bias_estimation) +{ + do_bias_estimation_ = do_bias_estimation; +} + +bool ComplementaryFilter::getDoBiasEstimation() const +{ + return do_bias_estimation_; +} + +void ComplementaryFilter::setDoAdaptiveGain(bool do_adaptive_gain) +{ + do_adaptive_gain_ = do_adaptive_gain; +} + +bool ComplementaryFilter::getDoAdaptiveGain() const +{ + return do_adaptive_gain_; +} + +bool ComplementaryFilter::setGainAcc(double gain) +{ + if (gain >= 0 && gain <= 1.0) + { + gain_acc_ = gain; + return true; + } else + return false; +} +bool ComplementaryFilter::setGainMag(double gain) +{ + if (gain >= 0 && gain <= 1.0) + { + gain_mag_ = gain; + return true; + } else + return false; +} + +double ComplementaryFilter::getGainAcc() const +{ + return gain_acc_; +} + +double ComplementaryFilter::getGainMag() const +{ + return gain_mag_; +} + +bool ComplementaryFilter::getSteadyState() const +{ + return steady_state_; +} + +bool ComplementaryFilter::setBiasAlpha(double bias_alpha) +{ + if (bias_alpha >= 0 && bias_alpha <= 1.0) + { + bias_alpha_ = bias_alpha; + return true; + } else + return false; +} + +double ComplementaryFilter::getBiasAlpha() const +{ + return bias_alpha_; +} + +void ComplementaryFilter::setOrientation(double q0, double q1, double q2, + double q3) +{ + // Set the state to inverse (state is fixed wrt body). + invertQuaternion(q0, q1, q2, q3, q0_, q1_, q2_, q3_); +} + +double ComplementaryFilter::getAngularVelocityBiasX() const +{ + return wx_bias_; +} + +double ComplementaryFilter::getAngularVelocityBiasY() const +{ + return wy_bias_; +} + +double ComplementaryFilter::getAngularVelocityBiasZ() const +{ + return wz_bias_; +} + +void ComplementaryFilter::update(double ax, double ay, double az, double wx, + double wy, double wz, double dt) +{ + if (!initialized_) + { + // First time - ignore prediction: + getMeasurement(ax, ay, az, q0_, q1_, q2_, q3_); + initialized_ = true; + return; + } + + // Bias estimation. + if (do_bias_estimation_) updateBiases(ax, ay, az, wx, wy, wz); + + // Prediction. + double q0_pred, q1_pred, q2_pred, q3_pred; + getPrediction(wx, wy, wz, dt, q0_pred, q1_pred, q2_pred, q3_pred); + + // Correction (from acc): + // q_ = q_pred * [(1-gain) * qI + gain * dq_acc] + // where qI = identity quaternion + double dq0_acc, dq1_acc, dq2_acc, dq3_acc; + getAccCorrection(ax, ay, az, q0_pred, q1_pred, q2_pred, q3_pred, dq0_acc, + dq1_acc, dq2_acc, dq3_acc); + + double gain; + if (do_adaptive_gain_) + { + gain = getAdaptiveGain(gain_acc_, ax, ay, az); + + } else + { + gain = gain_acc_; + } + + scaleQuaternion(gain, dq0_acc, dq1_acc, dq2_acc, dq3_acc); + + quaternionMultiplication(q0_pred, q1_pred, q2_pred, q3_pred, dq0_acc, + dq1_acc, dq2_acc, dq3_acc, q0_, q1_, q2_, q3_); + + normalizeQuaternion(q0_, q1_, q2_, q3_); +} + +void ComplementaryFilter::update(double ax, double ay, double az, double wx, + double wy, double wz, double mx, double my, + double mz, double dt) +{ + if (!initialized_) + { + // First time - ignore prediction: + getMeasurement(ax, ay, az, mx, my, mz, q0_, q1_, q2_, q3_); + initialized_ = true; + return; + } + + // Bias estimation. + if (do_bias_estimation_) updateBiases(ax, ay, az, wx, wy, wz); + + // Prediction. + double q0_pred, q1_pred, q2_pred, q3_pred; + getPrediction(wx, wy, wz, dt, q0_pred, q1_pred, q2_pred, q3_pred); + + // Correction (from acc): + // q_temp = q_pred * [(1-gain) * qI + gain * dq_acc] + // where qI = identity quaternion + double dq0_acc, dq1_acc, dq2_acc, dq3_acc; + getAccCorrection(ax, ay, az, q0_pred, q1_pred, q2_pred, q3_pred, dq0_acc, + dq1_acc, dq2_acc, dq3_acc); + double alpha = gain_acc_; + if (do_adaptive_gain_) alpha = getAdaptiveGain(gain_acc_, ax, ay, az); + scaleQuaternion(alpha, dq0_acc, dq1_acc, dq2_acc, dq3_acc); + + double q0_temp, q1_temp, q2_temp, q3_temp; + quaternionMultiplication(q0_pred, q1_pred, q2_pred, q3_pred, dq0_acc, + dq1_acc, dq2_acc, dq3_acc, q0_temp, q1_temp, + q2_temp, q3_temp); + + // Correction (from mag): + // q_ = q_temp * [(1-gain) * qI + gain * dq_mag] + // where qI = identity quaternion + double dq0_mag, dq1_mag, dq2_mag, dq3_mag; + getMagCorrection(mx, my, mz, q0_temp, q1_temp, q2_temp, q3_temp, dq0_mag, + dq1_mag, dq2_mag, dq3_mag); + + scaleQuaternion(gain_mag_, dq0_mag, dq1_mag, dq2_mag, dq3_mag); + + quaternionMultiplication(q0_temp, q1_temp, q2_temp, q3_temp, dq0_mag, + dq1_mag, dq2_mag, dq3_mag, q0_, q1_, q2_, q3_); + + normalizeQuaternion(q0_, q1_, q2_, q3_); +} + +bool ComplementaryFilter::checkState(double ax, double ay, double az, double wx, + double wy, double wz) const +{ + double acc_magnitude = sqrt(ax * ax + ay * ay + az * az); + if (fabs(acc_magnitude - kGravity) > kAccelerationThreshold) return false; + + if (fabs(wx - wx_prev_) > kDeltaAngularVelocityThreshold || + fabs(wy - wy_prev_) > kDeltaAngularVelocityThreshold || + fabs(wz - wz_prev_) > kDeltaAngularVelocityThreshold) + return false; + + if (fabs(wx - wx_bias_) > kAngularVelocityThreshold || + fabs(wy - wy_bias_) > kAngularVelocityThreshold || + fabs(wz - wz_bias_) > kAngularVelocityThreshold) + return false; + + return true; +} + +void ComplementaryFilter::updateBiases(double ax, double ay, double az, + double wx, double wy, double wz) +{ + steady_state_ = checkState(ax, ay, az, wx, wy, wz); + + if (steady_state_) + { + wx_bias_ += bias_alpha_ * (wx - wx_bias_); + wy_bias_ += bias_alpha_ * (wy - wy_bias_); + wz_bias_ += bias_alpha_ * (wz - wz_bias_); + } + + wx_prev_ = wx; + wy_prev_ = wy; + wz_prev_ = wz; +} + +void ComplementaryFilter::getPrediction(double wx, double wy, double wz, + double dt, double& q0_pred, + double& q1_pred, double& q2_pred, + double& q3_pred) const +{ + double wx_unb = wx - wx_bias_; + double wy_unb = wy - wy_bias_; + double wz_unb = wz - wz_bias_; + + q0_pred = q0_ + 0.5 * dt * (wx_unb * q1_ + wy_unb * q2_ + wz_unb * q3_); + q1_pred = q1_ + 0.5 * dt * (-wx_unb * q0_ - wy_unb * q3_ + wz_unb * q2_); + q2_pred = q2_ + 0.5 * dt * (wx_unb * q3_ - wy_unb * q0_ - wz_unb * q1_); + q3_pred = q3_ + 0.5 * dt * (-wx_unb * q2_ + wy_unb * q1_ - wz_unb * q0_); + + normalizeQuaternion(q0_pred, q1_pred, q2_pred, q3_pred); +} + +void ComplementaryFilter::getMeasurement(double ax, double ay, double az, + double mx, double my, double mz, + double& q0_meas, double& q1_meas, + double& q2_meas, double& q3_meas) +{ + // q_acc is the quaternion obtained from the acceleration vector + // representing the orientation of the Global frame wrt the Local frame with + // arbitrary yaw (intermediary frame). q3_acc is defined as 0. + double q0_acc, q1_acc, q2_acc, q3_acc; + + // Normalize acceleration vector. + normalizeVector(ax, ay, az); + if (az >= 0) + { + q0_acc = sqrt((az + 1) * 0.5); + q1_acc = -ay / (2.0 * q0_acc); + q2_acc = ax / (2.0 * q0_acc); + q3_acc = 0; + } else + { + double X = sqrt((1 - az) * 0.5); + q0_acc = -ay / (2.0 * X); + q1_acc = X; + q2_acc = 0; + q3_acc = ax / (2.0 * X); + } + + // [lx, ly, lz] is the magnetic field reading, rotated into the intermediary + // frame by the inverse of q_acc. + // l = R(q_acc)^-1 m + double lx = (q0_acc * q0_acc + q1_acc * q1_acc - q2_acc * q2_acc) * mx + + 2.0 * (q1_acc * q2_acc) * my - 2.0 * (q0_acc * q2_acc) * mz; + double ly = 2.0 * (q1_acc * q2_acc) * mx + + (q0_acc * q0_acc - q1_acc * q1_acc + q2_acc * q2_acc) * my + + 2.0 * (q0_acc * q1_acc) * mz; + + // q_mag is the quaternion that rotates the Global frame (North West Up) + // into the intermediary frame. q1_mag and q2_mag are defined as 0. + double gamma = lx * lx + ly * ly; + double beta = sqrt(gamma + lx * sqrt(gamma)); + double q0_mag = beta / (sqrt(2.0 * gamma)); + double q3_mag = ly / (sqrt(2.0) * beta); + + // The quaternion multiplication between q_acc and q_mag represents the + // quaternion, orientation of the Global frame wrt the local frame. + // q = q_acc times q_mag + quaternionMultiplication(q0_acc, q1_acc, q2_acc, q3_acc, q0_mag, 0, 0, + q3_mag, q0_meas, q1_meas, q2_meas, q3_meas); + // q0_meas = q0_acc*q0_mag; + // q1_meas = q1_acc*q0_mag + q2_acc*q3_mag; + // q2_meas = q2_acc*q0_mag - q1_acc*q3_mag; + // q3_meas = q0_acc*q3_mag; +} + +void ComplementaryFilter::getMeasurement(double ax, double ay, double az, + double& q0_meas, double& q1_meas, + double& q2_meas, double& q3_meas) +{ + // q_acc is the quaternion obtained from the acceleration vector + // representing the orientation of the Global frame wrt the Local frame with + // arbitrary yaw (intermediary frame). q3_acc is defined as 0. + + // Normalize acceleration vector. + normalizeVector(ax, ay, az); + + if (az >= 0) + { + q0_meas = sqrt((az + 1) * 0.5); + q1_meas = -ay / (2.0 * q0_meas); + q2_meas = ax / (2.0 * q0_meas); + q3_meas = 0; + } else + { + double X = sqrt((1 - az) * 0.5); + q0_meas = -ay / (2.0 * X); + q1_meas = X; + q2_meas = 0; + q3_meas = ax / (2.0 * X); + } +} + +void ComplementaryFilter::getAccCorrection(double ax, double ay, double az, + double p0, double p1, double p2, + double p3, double& dq0, double& dq1, + double& dq2, double& dq3) +{ + // Normalize acceleration vector. + normalizeVector(ax, ay, az); + + // Acceleration reading rotated into the world frame by the inverse + // predicted quaternion (predicted gravity): + double gx, gy, gz; + rotateVectorByQuaternion(ax, ay, az, p0, -p1, -p2, -p3, gx, gy, gz); + + // Delta quaternion that rotates the predicted gravity into the real + // gravity: + dq0 = sqrt((gz + 1) * 0.5); + dq1 = -gy / (2.0 * dq0); + dq2 = gx / (2.0 * dq0); + dq3 = 0.0; +} + +void ComplementaryFilter::getMagCorrection(double mx, double my, double mz, + double p0, double p1, double p2, + double p3, double& dq0, double& dq1, + double& dq2, double& dq3) +{ + // Magnetic reading rotated into the world frame by the inverse predicted + // quaternion: + double lx, ly, lz; + rotateVectorByQuaternion(mx, my, mz, p0, -p1, -p2, -p3, lx, ly, lz); + + // Delta quaternion that rotates the l so that it lies in the xz-plane + // (points north): + double gamma = lx * lx + ly * ly; + double beta = sqrt(gamma + lx * sqrt(gamma)); + dq0 = beta / (sqrt(2.0 * gamma)); + dq1 = 0.0; + dq2 = 0.0; + dq3 = ly / (sqrt(2.0) * beta); +} + +void ComplementaryFilter::getOrientation(double& q0, double& q1, double& q2, + double& q3) const +{ + // Return the inverse of the state (state is fixed wrt body). + invertQuaternion(q0_, q1_, q2_, q3_, q0, q1, q2, q3); +} + +double ComplementaryFilter::getAdaptiveGain(double alpha, double ax, double ay, + double az) +{ + double a_mag = sqrt(ax * ax + ay * ay + az * az); + double error = fabs(a_mag - kGravity) / kGravity; + double factor; + double error1 = 0.1; + double error2 = 0.2; + double m = 1.0 / (error1 - error2); + double b = 1.0 - m * error1; + if (error < error1) + factor = 1.0; + else if (error < error2) + factor = m * error + b; + else + factor = 0.0; + // printf("FACTOR: %f \n", factor); + return factor * alpha; +} + +void normalizeVector(double& x, double& y, double& z) +{ + double norm = sqrt(x * x + y * y + z * z); + + x /= norm; + y /= norm; + z /= norm; +} + +void normalizeQuaternion(double& q0, double& q1, double& q2, double& q3) +{ + double norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 /= norm; + q1 /= norm; + q2 /= norm; + q3 /= norm; +} + +void invertQuaternion(double q0, double q1, double q2, double q3, + double& q0_inv, double& q1_inv, double& q2_inv, + double& q3_inv) +{ + // Assumes quaternion is normalized. + q0_inv = q0; + q1_inv = -q1; + q2_inv = -q2; + q3_inv = -q3; +} + +void scaleQuaternion(double gain, double& dq0, double& dq1, double& dq2, + double& dq3) +{ + if (dq0 < 0.0) // 0.9 + { + // Slerp (Spherical linear interpolation): + double angle = acos(dq0); + double A = sin(angle * (1.0 - gain)) / sin(angle); + double B = sin(angle * gain) / sin(angle); + dq0 = A + B * dq0; + dq1 = B * dq1; + dq2 = B * dq2; + dq3 = B * dq3; + } else + { + // Lerp (Linear interpolation): + dq0 = (1.0 - gain) + gain * dq0; + dq1 = gain * dq1; + dq2 = gain * dq2; + dq3 = gain * dq3; + } + + normalizeQuaternion(dq0, dq1, dq2, dq3); +} + +void quaternionMultiplication(double p0, double p1, double p2, double p3, + double q0, double q1, double q2, double q3, + double& r0, double& r1, double& r2, double& r3) +{ + // r = p q + r0 = p0 * q0 - p1 * q1 - p2 * q2 - p3 * q3; + r1 = p0 * q1 + p1 * q0 + p2 * q3 - p3 * q2; + r2 = p0 * q2 - p1 * q3 + p2 * q0 + p3 * q1; + r3 = p0 * q3 + p1 * q2 - p2 * q1 + p3 * q0; +} + +void rotateVectorByQuaternion(double x, double y, double z, double q0, + double q1, double q2, double q3, double& vx, + double& vy, double& vz) +{ + vx = (q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * x + + 2 * (q1 * q2 - q0 * q3) * y + 2 * (q1 * q3 + q0 * q2) * z; + vy = 2 * (q1 * q2 + q0 * q3) * x + + (q0 * q0 - q1 * q1 + q2 * q2 - q3 * q3) * y + + 2 * (q2 * q3 - q0 * q1) * z; + vz = 2 * (q1 * q3 - q0 * q2) * x + 2 * (q2 * q3 + q0 * q1) * y + + (q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3) * z; +} + +} // namespace imu_tools diff --git a/src/rm_perception/imu_complementary_filter/src/complementary_filter_node.cpp b/src/rm_perception/imu_complementary_filter/src/complementary_filter_node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f902841f2a2ab2c549ee63ded02f5834f3f8f0c --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/src/complementary_filter_node.cpp @@ -0,0 +1,43 @@ +/* + @author Roberto G. Valenti + + @section LICENSE + Copyright (c) 2015, City University of New York + CCNY Robotics Lab + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "imu_complementary_filter/complementary_filter_ros.h" + +int main(int argc, char **argv) +{ + rclcpp::init(argc, argv); + auto filter = std::make_shared(); + rclcpp::spin(filter); + rclcpp::shutdown(); + return 0; +} diff --git a/src/rm_perception/imu_complementary_filter/src/complementary_filter_ros.cpp b/src/rm_perception/imu_complementary_filter/src/complementary_filter_ros.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8a9dd7465f4fe2827596ee66624be2ec25e5778 --- /dev/null +++ b/src/rm_perception/imu_complementary_filter/src/complementary_filter_ros.cpp @@ -0,0 +1,305 @@ +/* + @author Roberto G. Valenti + + @section LICENSE + Copyright (c) 2015, City University of New York + CCNY Robotics Lab + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "imu_complementary_filter/complementary_filter_ros.h" + +#include +#include +#include +#include +#include + +namespace imu_tools { + +ComplementaryFilterROS::ComplementaryFilterROS() + : Node("ComplementaryFilterROS"), + tf_broadcaster_(this), + initialized_filter_(false) +{ + RCLCPP_INFO(this->get_logger(), "Starting ComplementaryFilterROS"); + initializeParams(); + + int queue_size = 5; + + // Register publishers: + // TODO: Check why ros::names::resolve is need here + imu_publisher_ = this->create_publisher("imu/data", queue_size); + + if (publish_debug_topics_) + { + rpy_publisher_ = + this->create_publisher( + "imu/rpy/filtered", queue_size); + + if (filter_.getDoBiasEstimation()) + { + state_publisher_ = this->create_publisher( + "/imu/steady_state", queue_size); + } + } + + // Register IMU raw data subscriber. + imu_subscriber_.reset(new ImuSubscriber(this, "/imu/data_raw")); + + // Register magnetic data subscriber. + if (use_mag_) + { + mag_subscriber_.reset(new MagSubscriber(this, "/imu/mag")); + + sync_.reset(new Synchronizer(SyncPolicy(queue_size), *imu_subscriber_, + *mag_subscriber_)); + sync_->registerCallback(&ComplementaryFilterROS::imuMagCallback, this); + } else + { + imu_subscriber_->registerCallback(&ComplementaryFilterROS::imuCallback, + this); + } +} + +ComplementaryFilterROS::~ComplementaryFilterROS() +{ + RCLCPP_INFO(this->get_logger(), "Destroying ComplementaryFilterROS"); +} + +void ComplementaryFilterROS::initializeParams() +{ + double gain_acc; + double gain_mag; + bool do_bias_estimation; + double bias_alpha; + bool do_adaptive_gain; + double orientation_stddev; + + fixed_frame_ = this->declare_parameter("fixed_frame", "odom"); + use_mag_ = this->declare_parameter("use_mag", false); + publish_tf_ = this->declare_parameter("publish_tf", false); + reverse_tf_ = this->declare_parameter("reverse_tf", false); + constant_dt_ = this->declare_parameter("constant_dt", 0.0); + publish_debug_topics_ = + this->declare_parameter("publish_debug_topics", false); + gain_acc = this->declare_parameter("gain_acc", 0.01); + gain_mag = this->declare_parameter("gain_mag", 0.01); + do_bias_estimation = + this->declare_parameter("do_bias_estimation", true); + bias_alpha = this->declare_parameter("bias_alpha", 0.01); + do_adaptive_gain = this->declare_parameter("do_adaptive_gain", true); + orientation_stddev = + this->declare_parameter("orientation_stddev", 0.0); + orientation_variance_ = orientation_stddev * orientation_stddev; + + filter_.setDoBiasEstimation(do_bias_estimation); + filter_.setDoAdaptiveGain(do_adaptive_gain); + + if (!filter_.setGainAcc(gain_acc)) + RCLCPP_WARN(this->get_logger(), + "Invalid gain_acc passed to ComplementaryFilter."); + if (use_mag_) + { + if (!filter_.setGainMag(gain_mag)) + RCLCPP_WARN(this->get_logger(), + "Invalid gain_mag passed to ComplementaryFilter."); + } + if (do_bias_estimation) + { + if (!filter_.setBiasAlpha(bias_alpha)) + RCLCPP_WARN(this->get_logger(), + "Invalid bias_alpha passed to ComplementaryFilter."); + } + + // check for illegal constant_dt values + if (constant_dt_ < 0.0) + { + // if constant_dt_ is 0.0 (default), use IMU timestamp to determine dt + // otherwise, it will be constant + RCLCPP_WARN( + this->get_logger(), + "constant_dt parameter is %f, must be >= 0.0. Setting to 0.0", + constant_dt_); + constant_dt_ = 0.0; + } +} + +void ComplementaryFilterROS::imuCallback(ImuMsg::ConstSharedPtr imu_msg_raw) +{ + const geometry_msgs::msg::Vector3 &a = imu_msg_raw->linear_acceleration; + const geometry_msgs::msg::Vector3 &w = imu_msg_raw->angular_velocity; + const rclcpp::Time &time = imu_msg_raw->header.stamp; + + // Initialize. + if (!initialized_filter_) + { + time_prev_ = time; + initialized_filter_ = true; + return; + } + + // determine dt: either constant, or from IMU timestamp + double dt; + if (constant_dt_ > 0.0) + dt = constant_dt_; + else + dt = (time - time_prev_).nanoseconds() * 1e-9; + + time_prev_ = time; + + // Update the filter. + filter_.update(a.x, a.y, a.z, w.x, w.y, w.z, dt); + + // Publish state. + publish(imu_msg_raw); +} + +void ComplementaryFilterROS::imuMagCallback(ImuMsg::ConstSharedPtr imu_msg_raw, + MagMsg::ConstSharedPtr mag_msg) +{ + const geometry_msgs::msg::Vector3 &a = imu_msg_raw->linear_acceleration; + const geometry_msgs::msg::Vector3 &w = imu_msg_raw->angular_velocity; + const geometry_msgs::msg::Vector3 &m = mag_msg->magnetic_field; + const rclcpp::Time &time = imu_msg_raw->header.stamp; + + // Initialize. + if (!initialized_filter_) + { + time_prev_ = time; + initialized_filter_ = true; + return; + } + + // Calculate dt. + double dt = (time - time_prev_).nanoseconds() * 1e-9; + time_prev_ = time; + // ros::Time t_in, t_out; + // t_in = ros::Time::now(); + // Update the filter. + if (std::isnan(m.x) || std::isnan(m.y) || std::isnan(m.z)) + filter_.update(a.x, a.y, a.z, w.x, w.y, w.z, dt); + else + filter_.update(a.x, a.y, a.z, w.x, w.y, w.z, m.x, m.y, m.z, dt); + + // t_out = ros::Time::now(); + // float dt_tot = (t_out - t_in).toSec() * 1000.0; // In msec. + // printf("%.6f\n", dt_tot); + // Publish state. + publish(imu_msg_raw); +} + +tf2::Quaternion ComplementaryFilterROS::hamiltonToTFQuaternion(double q0, + double q1, + double q2, + double q3) const +{ + // ROS uses the Hamilton quaternion convention (q0 is the scalar). However, + // the ROS quaternion is in the form [x, y, z, w], with w as the scalar. + return tf2::Quaternion(q1, q2, q3, q0); +} + +void ComplementaryFilterROS::publish(ImuMsg::ConstSharedPtr imu_msg_raw) +{ + // Get the orientation: + double q0, q1, q2, q3; + filter_.getOrientation(q0, q1, q2, q3); + tf2::Quaternion q = hamiltonToTFQuaternion(q0, q1, q2, q3); + + // Create and publish fitlered IMU message. + ImuMsg::SharedPtr imu_msg = std::make_shared(*imu_msg_raw); + imu_msg->orientation.x = q1; + imu_msg->orientation.y = q2; + imu_msg->orientation.z = q3; + imu_msg->orientation.w = q0; + + imu_msg->orientation_covariance[0] = orientation_variance_; + imu_msg->orientation_covariance[1] = 0.0; + imu_msg->orientation_covariance[2] = 0.0; + imu_msg->orientation_covariance[3] = 0.0; + imu_msg->orientation_covariance[4] = orientation_variance_; + imu_msg->orientation_covariance[5] = 0.0; + imu_msg->orientation_covariance[6] = 0.0; + imu_msg->orientation_covariance[7] = 0.0; + imu_msg->orientation_covariance[8] = orientation_variance_; + + // Account for biases. + if (filter_.getDoBiasEstimation()) + { + imu_msg->angular_velocity.x -= filter_.getAngularVelocityBiasX(); + imu_msg->angular_velocity.y -= filter_.getAngularVelocityBiasY(); + imu_msg->angular_velocity.z -= filter_.getAngularVelocityBiasZ(); + } + + imu_publisher_->publish(*imu_msg); + + if (publish_debug_topics_) + { + // Create and publish roll, pitch, yaw angles + geometry_msgs::msg::Vector3Stamped rpy; + rpy.header = imu_msg_raw->header; + + tf2::Matrix3x3 M; + M.setRotation(q); + M.getRPY(rpy.vector.x, rpy.vector.y, rpy.vector.z); + rpy_publisher_->publish(rpy); + + // Publish whether we are in the steady state, when doing bias + // estimation + if (filter_.getDoBiasEstimation()) + { + std_msgs::msg::Bool state_msg; + state_msg.data = filter_.getSteadyState(); + state_publisher_->publish(state_msg); + } + } + + if (publish_tf_) + { + // Create and publish the ROS tf. + geometry_msgs::msg::TransformStamped transform; + transform.header.stamp = imu_msg_raw->header.stamp; + transform.transform.rotation.x = q1; + transform.transform.rotation.y = q2; + transform.transform.rotation.z = q3; + transform.transform.rotation.w = q0; + + if (reverse_tf_) + { + transform.header.frame_id = imu_msg_raw->header.frame_id; + transform.child_frame_id = fixed_frame_; + tf_broadcaster_.sendTransform(transform); + } else + { + transform.child_frame_id = imu_msg_raw->header.frame_id; + transform.header.frame_id = fixed_frame_; + tf_broadcaster_.sendTransform(transform); + } + } +} + +} // namespace imu_tools diff --git a/src/rm_perception/linefit_ground_segementation_ros2/linefit_ground_segmentation_ros/launch/segmentation_params.yaml b/src/rm_perception/linefit_ground_segementation_ros2/linefit_ground_segmentation_ros/launch/segmentation_params.yaml index 287e4fd6ca32a8a26c4670336e10c4e2c242afa3..3ccaa27d2fca345229aa7be5c9a254ce0739a255 100644 --- a/src/rm_perception/linefit_ground_segementation_ros2/linefit_ground_segmentation_ros/launch/segmentation_params.yaml +++ b/src/rm_perception/linefit_ground_segementation_ros2/linefit_ground_segmentation_ros/launch/segmentation_params.yaml @@ -23,6 +23,6 @@ ground_segmentation: latch: false # latch output topics or not visualize: false # visualize segmentation result - USE ONLY FOR DEBUGGING - input_topic: "/mid360_PointCloud2" + input_topic: "/livox/lidar_PointCloud2" obstacle_output_topic: "segmentation/obstacle" - ground_output_topic: "segmentation/ground" + ground_output_topic: "segmentation/ground" \ No newline at end of file diff --git a/src/rm_perception/pointcloud_to_laserscan/launch/pointcloud_to_laserscan_launch.py b/src/rm_perception/pointcloud_to_laserscan/launch/pointcloud_to_laserscan_launch.py index 79b145ab026608531d8e065009c5d7cb1bf02f1d..02f91197c9db0a68ee0ba2e53df853997d410b91 100644 --- a/src/rm_perception/pointcloud_to_laserscan/launch/pointcloud_to_laserscan_launch.py +++ b/src/rm_perception/pointcloud_to_laserscan/launch/pointcloud_to_laserscan_launch.py @@ -15,7 +15,7 @@ def generate_launch_description(): remappings=[('cloud_in', ['/segmentation/obstacle']), ('scan', ['/scan'])], parameters=[{ - # 'target_frame': 'livox_frame', + 'target_frame': 'livox_frame', 'transform_tolerance': 0.01, 'min_height': -1.0, 'max_height': 0.1, @@ -30,4 +30,4 @@ def generate_launch_description(): }], name='pointcloud_to_laserscan' ) - ]) + ]) \ No newline at end of file diff --git a/src/rm_simulation/livox_laser_simulation_RO2/src/livox_points_plugin.cpp b/src/rm_simulation/livox_laser_simulation_RO2/src/livox_points_plugin.cpp index 77a78b40682b6087fb8e6cf4e770dcc896b6c6cb..d6babbf24f1d3c7a2f7e2399d62c7e4141aef231 100644 --- a/src/rm_simulation/livox_laser_simulation_RO2/src/livox_points_plugin.cpp +++ b/src/rm_simulation/livox_laser_simulation_RO2/src/livox_points_plugin.cpp @@ -123,104 +123,87 @@ namespace gazebo } } - void LivoxPointsPlugin::OnNewLaserScans() - { - // 检查是否已经初始化了 rayShape - if (rayShape) - { - std::vector> points_pair; - // 初始化射线扫描点对 - InitializeRays(points_pair, rayShape); - rayShape->Update(); - - // 创建激光扫描消息并设置时间戳 - msgs::Set(laserMsg.mutable_time(), world->SimTime()); - msgs::LaserScan *scan = laserMsg.mutable_scan(); - InitializeScan(scan); - - // 创建自定义消息 pp_livox,用于发布 Livox CustomMsg 类型消息 - livox_ros_driver2::msg::CustomMsg pp_livox; - pp_livox.header.stamp = node_->get_clock()->now(); - pp_livox.header.frame_id = raySensor->Name(); - int count = 0; - boost::chrono::high_resolution_clock::time_point start_time = boost::chrono::high_resolution_clock::now(); - - // 用于 PointCloud2 类型消息发布 - sensor_msgs::msg::PointCloud cloud; - cloud.header.stamp = node_->get_clock()->now(); - cloud.header.frame_id = raySensor->Name(); - auto &clouds = cloud.points; - - // 遍历射线扫描点对 - for (auto &pair : points_pair) - { - auto range = rayShape->GetRange(pair.first); - auto intensity = rayShape->GetRetro(pair.first); - - // 处理超出范围的数据 - if (range >= RangeMax()) - { - range = 0; - } - else if (range <= RangeMin()) - { - range = 0; - } - - // 计算点云数据 - auto rotate_info = pair.second; - ignition::math::Quaterniond ray; - ray.Euler(ignition::math::Vector3d(0.0, rotate_info.zenith, rotate_info.azimuth)); - auto axis = ray * ignition::math::Vector3d(1.0, 0.0, 0.0); - auto point = range * axis; - - // 填充 CustomMsg 点云消息 - livox_ros_driver2::msg::CustomPoint p; - p.x = point.X(); - p.y = point.Y(); - p.z = point.Z(); - p.reflectivity = intensity; - - // 填充 PointCloud 点云消息 - clouds.emplace_back(); - clouds.back().x = point.X(); - clouds.back().y = point.Y(); - clouds.back().z = point.Z(); - - - // 填充 PointCloud 点云消息 - clouds.emplace_back(); - clouds.back().x = point.X(); - clouds.back().y = point.Y(); - clouds.back().z = point.Z(); - - - // 计算时间戳偏移 - boost::chrono::high_resolution_clock::time_point end_time = boost::chrono::high_resolution_clock::now(); - boost::chrono::nanoseconds elapsed_time = boost::chrono::duration_cast(end_time - start_time); - p.offset_time = elapsed_time.count(); - - // 将点云数据添加到 CustomMsg 消息中 - pp_livox.points.push_back(p); - count ++; - } - - if (scanPub && scanPub->HasConnections()) scanPub->Publish(laserMsg); + void LivoxPointsPlugin::OnNewLaserScans() { + if (!rayShape) { + return; // 检查是否已经初始化了 rayShape + } - // 设置点云数据数量并发布 CustomMsg 消息 - pp_livox.point_num = count; - custom_pub->publish(pp_livox); + std::vector> points_pair; + InitializeRays(points_pair, rayShape); + rayShape->Update(); + + msgs::Set(laserMsg.mutable_time(), world->SimTime()); + msgs::LaserScan *scan = laserMsg.mutable_scan(); + InitializeScan(scan); + + // 创建自定义消息 pp_livox,用于发布 Livox CustomMsg 类型消息 + livox_ros_driver2::msg::CustomMsg pp_livox; + pp_livox.header.stamp = node_->get_clock()->now(); + pp_livox.header.frame_id = raySensor->Name(); + int count = 0; + boost::chrono::high_resolution_clock::time_point start_time = boost::chrono::high_resolution_clock::now(); + + // 用于 PointCloud2 类型消息发布 + sensor_msgs::msg::PointCloud cloud; + cloud.header.stamp = node_->get_clock()->now(); + cloud.header.frame_id = raySensor->Name(); + auto &clouds = cloud.points; + + // 遍历射线扫描点对 + for (const auto &pair : points_pair) { + auto range = rayShape->GetRange(pair.first); + auto intensity = rayShape->GetRetro(pair.first); + + // 处理超出范围的数据 + if (range <= RangeMin() || range >= RangeMax()) { + range = 0; + } - // 发布 Pointcloud2 类型消息 - sensor_msgs::msg::PointCloud2 cloud2; - sensor_msgs::convertPointCloudToPointCloud2(cloud, cloud2); - cloud2.header = cloud.header; - cloud2_pub->publish(cloud2); + // 计算点云数据 + auto rotate_info = pair.second; + ignition::math::Quaterniond ray; + ray.Euler(ignition::math::Vector3d(0.0, rotate_info.zenith, rotate_info.azimuth)); + auto axis = ray * ignition::math::Vector3d(1.0, 0.0, 0.0); + auto point = range * axis; + + // 填充 CustomMsg 点云消息 + livox_ros_driver2::msg::CustomPoint p; + p.x = point.X(); + p.y = point.Y(); + p.z = point.Z(); + p.reflectivity = intensity; + + // 填充 PointCloud 点云消息 + clouds.emplace_back(); + clouds.back().x = point.X(); + clouds.back().y = point.Y(); + clouds.back().z = point.Z(); + + // 计算时间戳偏移 + boost::chrono::high_resolution_clock::time_point end_time = boost::chrono::high_resolution_clock::now(); + boost::chrono::nanoseconds elapsed_time = boost::chrono::duration_cast(end_time - start_time); + p.offset_time = elapsed_time.count(); + + // 将点云数据添加到 CustomMsg 消息中 + pp_livox.points.push_back(p); + count++; + } + if (scanPub && scanPub->HasConnections()) { + scanPub->Publish(laserMsg); } + + // 发布 CustomMsg 消息 + pp_livox.point_num = count; + custom_pub->publish(pp_livox); + + // 发布 Pointcloud2 类型消息 + sensor_msgs::msg::PointCloud2 cloud2; + sensor_msgs::convertPointCloudToPointCloud2(cloud, cloud2); + cloud2.header = cloud.header; + cloud2_pub->publish(cloud2); } - void LivoxPointsPlugin::InitializeRays(std::vector> &points_pair, boost::shared_ptr &ray_shape) { @@ -403,4 +386,4 @@ namespace gazebo } -} +} \ No newline at end of file diff --git a/src/rm_simulation/livox_laser_simulation_RO2/urdf/mid360.xacro b/src/rm_simulation/livox_laser_simulation_RO2/urdf/mid360.xacro index 60f681eae6800ca7672a37baadde6b929fe09c82..7f068636abebd82b10cd848437c790f5a9a97da8 100644 --- a/src/rm_simulation/livox_laser_simulation_RO2/urdf/mid360.xacro +++ b/src/rm_simulation/livox_laser_simulation_RO2/urdf/mid360.xacro @@ -27,11 +27,12 @@ - - - - - + + + + + + diff --git a/src/rm_simulation/pb_rm_simulation/meshes/mid360.stl b/src/rm_simulation/pb_rm_simulation/meshes/mid360.stl new file mode 100644 index 0000000000000000000000000000000000000000..5c7fdb786e061c00994700924ca2af4b26ed3824 Binary files /dev/null and b/src/rm_simulation/pb_rm_simulation/meshes/mid360.stl differ diff --git a/src/rm_simulation/pb_rm_simulation/urdf/waking_robot.xacro b/src/rm_simulation/pb_rm_simulation/urdf/waking_robot.xacro index 3a7f4eb66ea627edcc85dc93f4917c760508208a..5696af5b645cbe3b0ad31143c21fc49fded161b9 100644 --- a/src/rm_simulation/pb_rm_simulation/urdf/waking_robot.xacro +++ b/src/rm_simulation/pb_rm_simulation/urdf/waking_robot.xacro @@ -13,26 +13,6 @@ - - - - - - - - - - - Gazebo/Green - false - - - - - - - - @@ -61,14 +41,6 @@ - @@ -87,14 +59,6 @@ - @@ -113,14 +77,6 @@ - @@ -139,14 +95,6 @@ - @@ -246,23 +194,24 @@ - true - 20.0 - / - cmd_vel - odom - odom + + + cmd_vel:=cmd_vel + odom:=odom + + + 100 + 100 + + false + false + base_to_wheel1 base_to_wheel3 base_to_wheel2 base_to_wheel4 0.26 0.12 - 20.0 - 2 - base_footprint - true - true @@ -276,7 +225,7 @@ true __default_topic__ - imu + /livox/imu imu_link 100.0 0.0 @@ -289,7 +238,7 @@ 0.0 0.0 - ~/out:=/imu + ~/out:=/livox/imu @@ -297,7 +246,7 @@ - +