diff --git a/.gitignore b/.gitignore
index e69de29..5079921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,47 @@
+# ROS 2 / Colcon
+build/
+install/
+log/
+
+# CMake
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
+Makefile
+CTestTestfile.cmake
+Testing/
+
+# C++ 编译输出
+*.o
+*.obj
+*.so
+*.a
+*.dll
+*.exe
+*.out
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+
+# 系统文件
+.DS_Store
+Thumbs.db
+
+# 临时文件
+*.tmp
+*.temp
+*.log
+
+# 保留 compile_commands.json 给 clangd 使用
+# (不忽略 build/compile_commands.json,通过 CMake 导出到项目根目录)
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index aa46b6d..2a31b6f 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,8 +1,28 @@
{
- "clangd.arguments": [
+ // Clangd 配置
+ "clangd.path": "/usr/bin/clangd",
+ "clangd.arguments": [
+ "--background-index",
"--compile-commands-dir=${workspaceFolder}/build",
- "--completion-style=detailed",
- "--query-driver=/usr/bin/clang",
- "--header-insertion=never"
+ "--header-insertion=iwyu",
+ "--completion-style=bundled",
+ "--pch-storage=memory",
+ "--cross-file-rename"
],
+ // C++ 配置
+ "C_Cpp.intelliSenseEngine": "disabled", // 使用 clangd 替代默认引擎
+ "C_Cpp.autocomplete": "disabled",
+ "C_Cpp.errorSquiggles": "disabled",
+ // 文件关联
+ "files.associations": {
+ "*.h": "c",
+ "*.hpp": "cpp",
+ "*.cpp": "cpp"
+ },
+ // 编辑器配置
+ "editor.formatOnSave": true,
+ "editor.tabSize": 4,
+ "editor.insertSpaces": true,
+ // ROS 2 配置
+ "ros.distro": "humble"
}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..92a135f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,59 @@
+cmake_minimum_required(VERSION 3.8)
+project(amadeus_26)
+
+# 使用 C++17
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# 导出 compile_commands.json 给 clangd 使用
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# 查找依赖
+find_package(ament_cmake REQUIRED)
+find_package(rclcpp REQUIRED)
+find_package(std_msgs REQUIRED)
+find_package(geometry_msgs REQUIRED)
+find_package(sensor_msgs REQUIRED)
+
+# 添加 SDK 库路径
+set(TRANSMITTER_SDK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib/transmitter_sdk)
+link_directories(${TRANSMITTER_SDK_PATH}/lib/linux/x64)
+
+# 包含目录
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${TRANSMITTER_SDK_PATH}/inc
+)
+
+# ==================== UART 节点 ====================
+add_executable(uart_transmitter_node src/uart_transmitter_node.cpp)
+
+ament_target_dependencies(uart_transmitter_node
+ rclcpp
+ std_msgs
+)
+
+# ==================== IMU 节点 ====================
+add_executable(imu_receiver_node src/imu_receiver_node.cpp)
+
+ament_target_dependencies(imu_receiver_node
+ rclcpp
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+)
+
+# ==================== 安装目标 ====================
+install(TARGETS
+ uart_transmitter_node
+ imu_receiver_node
+ DESTINATION lib/${PROJECT_NAME}
+)
+
+# 安装 launch 文件
+install(DIRECTORY launch
+ DESTINATION share/${PROJECT_NAME}
+ OPTIONAL
+)
+
+ament_package()
\ No newline at end of file
diff --git a/README.md b/README.md
index 3c56262..b75c444 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,8 @@
- MV-SUA133GC-T 工业相机 (USB)
- NUC10i7 微型计算机 (主机)
-- FDI Systems DETA10 IMU 模块 (USB - CP210x 串口芯片 - UART) `/dev/ttyUSB0`
-- 达妙 USB2CAN-DUAL USB 转 CAN 模块 (USB - 模块 - CAN*2) `/dev/ttyACM0`
+- FDI Systems DETA10 IMU 模块 (USB - CP210x 串口芯片 - UART) `/dev/ttyCH340`
+- CH340 USB 转 UART 模块 (USB - 模块 - UART) `/dev/ttyIMU`
### 软件
@@ -19,12 +19,11 @@
- OpenCV 4.13 with Contrib
- Eigen 3
- MVSDK (工业相机 SDK)
-- dm_device (USB 转 CAN 模块 SDK)
- 待补充...
## 目标
-感谢同济 SuperPower 战队!自瞄算法参考同济的 sp_vision_26 。项目目标为使 2026 年采用的
+感谢同济 SuperPower 战队!自瞄算法参考同济的 sp_vision_26 。
## 软件架构
@@ -37,17 +36,22 @@ ROS2 基本架构为节点。以每个节点的发布、解析、接收数据为
- 发布:描述该节点使用 ROS 发布的数据
- 发送:描述该节点使用直连硬件发送的数据
-### 收发节点
+### UART 收发节点
- 功能:主机对外通信的封装节点。物理上读取收发模块
-- 连接:USB2CAN,协商 CAN 通信速率 1000kbps,`/dev/ttyACM0`
- IMU_CP210x,协商 UART 通信速率 921600,`/dev/ttyUSB0`
-- 接收:USB2CAN -> 裁判系统数据
+- 连接:CH340,协商 UART 通信速率 115200,`/dev/ttyCH340`
+- 接收:CH340.RX -> 裁判系统数据
- 订阅:中央节点 -> 控制指令
-- 发送:USB2CAN.CAN2 -> 控制指令
+- 发送:CH340.TX -> 控制指令
+- 发布:裁判系统数据(血量、比赛当前状态)
+ CH340 、IMU 连接 OK 标志
+
+### IMU 收发节点
+
+- 功能:IMU 与主机通信的封装节点。物理上读取 IMU(DETA10)数据
+- 连接:IMU_CP210x,协商 UART 通信速率 921600,`/dev/ttyUSB1`
+- 接收:IMU_CP210x -> 六轴加速度、陀螺仪数据
- 发布:IMU 数据(地磁角、六轴角加速度、加速度、陀螺仪)
- 裁判系统数据(血量、比赛当前状态)
- USB2CAN 、IMU 连接 OK 标志
### 中央节点
diff --git a/camera_sdk/lib/linux/arm64/libMVSDK.so b/camera_sdk/lib/linux/arm64/libMVSDK.so
deleted file mode 100644
index 402ea21..0000000
Binary files a/camera_sdk/lib/linux/arm64/libMVSDK.so and /dev/null differ
diff --git a/camera_sdk/lib/linux/x64/libMVSDK.so b/camera_sdk/lib/linux/x64/libMVSDK.so
deleted file mode 100644
index 625adcb..0000000
Binary files a/camera_sdk/lib/linux/x64/libMVSDK.so and /dev/null differ
diff --git a/docs/IMU/packet/MSG_AHRS.md b/docs/IMU/packet/MSG_AHRS.md
new file mode 100644
index 0000000..8b0e3cc
--- /dev/null
+++ b/docs/IMU/packet/MSG_AHRS.md
@@ -0,0 +1,27 @@
+# MSG_AHRS | FDISYSTEMS支持中心
+
+该数据包用于描述:卡尔曼滤波输出航姿参考系统数据
+
+## 基本信息
+
+| 属性 | 值 |
+|------|-----|
+| **Packet ID** | 0x41 |
+| **Length** | 48 |
+| **Read / Write** | Read |
+
+## 数据字段
+
+| Offset | Size | Format | Field | Unit | Description |
+|--------|------|--------|-------|------|-------------|
+| 0 | 4 | float32_t | RollSpeed | rad/s | 滤波修正后的横滚角速度,等于MSG_IMU数据包里的Gyroscope_X减去卡尔曼滤波估计的X轴角速度零偏 |
+| 4 | 4 | float32_t | PitchSpeed | rad/s | 滤波修正后的俯仰角速度,等于MSG_IMU数据包里的Gyroscope_Y减去卡尔曼滤波估计的Y轴角速度零偏 |
+| 8 | 4 | float32_t | HeadingSpeed | rad/s | 滤波修正后的偏航角速度,等于MSG_IMU数据包里的Gyroscope_Z减去卡尔曼滤波估计的Z轴角速度零偏 |
+| 12 | 4 | float32_t | Roll | rad | 横滚 |
+| 16 | 4 | float32_t | Pitch | rad | 俯仰 |
+| 20 | 4 | float32_t | Heading | rad | 偏航 |
+| 24 | 4 | float32_t | Q1 | / | 四元数Q1 |
+| 28 | 4 | float32_t | Q2 | / | 四元数Q2 |
+| 32 | 4 | float32_t | Q3 | / | 四元数Q3 |
+| 36 | 4 | float32_t | Q4 | / | 四元数Q4 |
+| 40 | 8 | int64_t | Timestamp | us | 数据的时间戳,从上电开始启动的微秒数。时钟源为MCU外部晶振。 |
\ No newline at end of file
diff --git a/docs/IMU/packet/MSG_BODY_ACCELERATION.md b/docs/IMU/packet/MSG_BODY_ACCELERATION.md
new file mode 100644
index 0000000..65e2d83
--- /dev/null
+++ b/docs/IMU/packet/MSG_BODY_ACCELERATION.md
@@ -0,0 +1,26 @@
+# MSG_BODY_ACCELERATION | FDISYSTEMS支持中心
+
+
+
+[Back to FDISYSTEMS](https://fdisystems.cn)
+
+Developers / FDILink协议 / Data Packets / MSG_BODY_ACCELERATION
+
+该数据包用于描述:滤波修正后的机体系加速度,不包括重力加速度。
+
+## 基本信息
+
+| 属性 | 值 |
+|------|-----|
+| **Packet ID** | 0x62 |
+| **Length** | 16 |
+| **Read / Write** | Read |
+
+## 数据字段
+
+| Offset | Size | Format | Field | Unit | Description |
+|--------|------|--------|-------|------|-------------|
+| 0 | 4 | float32_t | Body_acceleration_X | m/s/s | 滤波修正后的机体系X轴方向加速度 |
+| 4 | 4 | float32_t | Body_acceleration_Y | m/s/s | 滤波修正后的机体系Y轴方向加速度 |
+| 8 | 4 | float32_t | Body_acceleration_Z | m/s/s | 滤波修正后的机体系Z轴方向加速度 |
+| 12 | 4 | float32_t | G_force | m/s/s | 当地重力加速度 |
diff --git a/docs/IMU/packet/MSG_NED_VEL.md b/docs/IMU/packet/MSG_NED_VEL.md
new file mode 100644
index 0000000..91dd296
--- /dev/null
+++ b/docs/IMU/packet/MSG_NED_VEL.md
@@ -0,0 +1,19 @@
+# MSG_NED_VEL | FDISYSTEMS支持中心
+
+该数据包用于描述:卡尔曼滤波融合的北东地速度
+
+## 基本信息
+
+| 属性 | 值 |
+|------|-----|
+| **Packet ID** | 0x5F |
+| **Length** | 12 |
+| **Read / Write** | Read |
+
+## 数据字段
+
+| Offset | Size | Format | Field | Unit | Description |
+|--------|------|--------|-------|------|-------------|
+| 0 | 4 | float32_t | Velocity_north | m/s | 滤波修正的北向速度 |
+| 4 | 4 | float32_t | Velocity_east | m/s 7 | 滤波修正的东向速度 |
+| 8 | 4 | float32_t | Velocity_down | m/s | 滤波修正的地向速度 |
\ No newline at end of file
diff --git a/docs/Transmit/ctrl_command.md b/docs/Transmit/ctrl_command.md
deleted file mode 100644
index a1bdb81..0000000
--- a/docs/Transmit/ctrl_command.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# 控制指令的发送说明
-
-在主机外接收的控制板中的控制代码的实现是:
-
-```c
-// CAN 速率 1000kbps
-void CToC_CANDataProcess(unit32_t ID, uint8_t *Data)
-{
- // 以下所有变量有效范围为 [-660,660]
- if(ID == 0x149) // 运动控制
- {
-
- Remote_RxData.Remote_R_RL =(int16_t)((uint16_t)Data[0]<<8 | Data[1]); // 平动 X 轴(左右)
- Remote_RxData.Remote_R_UD =(int16_t)((uint16_t)Data[2]<<8 | Data[3]); // 平动 Y 轴(前后)
- Remote_RxData.Remote_L_RL =(int16_t)((uint16_t)Data[4]<<8 | Data[5]); // 云台偏航 (左右)
- Remote_RxData.Remote_L_UD =(int16_t)((uint16_t)Data[6]<<8 | Data[7]); // 云台俯仰 (上下)
- }
- else if(ID == 0x189) // 攻击控制
- {
- Remote_RxData.Remote_ThumbWheel =(int16_t)((uint16_t)Data[0]<<8 | Data[1]); // 拨弹盘(进弹/退弹),660 为进弹,0 为关闭,-660 为退弹
- Remote_RxData.Remote_RS =(int16_t)((uint16_t)Data[2]<<8 | Data[3]); // 小陀螺(反自瞄)开关,置 660 为开启,0 为关闭
- Remote_RxData.Remote_LS =(int16_t)((uint16_t)Data[4]<<8 | Data[5]); // 摩擦轮开关, 置 660 为开启,0 为关闭
- }
-}
-```
-
-两个 ID 分别是云台和底盘的控制板,在处于进点/回家状态时不应开启小陀螺,发射弹丸需要**打开摩擦轮 同时 进弹**,因此只有在处于攻击状态时开启摩擦轮,此时可以将进弹视为扳机。
\ No newline at end of file
diff --git a/docs/Transmit/ctrl_frame.md b/docs/Transmit/ctrl_frame.md
new file mode 100644
index 0000000..b066656
--- /dev/null
+++ b/docs/Transmit/ctrl_frame.md
@@ -0,0 +1,37 @@
+# SRCP 通信协议帧结构
+
+## 帧格式
+
+| 字段 | 内容 | 长度 | 说明 |
+|------|------|------|------|
+| SOF | 0xBB 0x77 | 2 bytes | 帧头 |
+| x_move | -660~660 | 2 bytes | 平动左右 (小端序) |
+| y_move | -660~660 | 2 bytes | 平动前后 (小端序) |
+| yaw | -660~660 | 2 bytes | 云台偏航 (小端序) |
+| pitch | -660~660 | 2 bytes | 云台俯仰 (小端序) |
+| feed | -660~660 | 2 bytes | 拨弹轮 (小端序) |
+| key | 0~15 | 1 byte | 按键 |
+| crc8 | CRC8-MAXIM | 1 byte | 校验 |
+| EOF | 0xEE | 1 byte | 帧尾 |
+
+## 帧长度
+
+```
+SOF(2) + x_move(2) + y_move(2) + yaw(2) + pitch(2) + feed(2) + key(1) + crc8(1) + EOF(1) = 15 bytes
+```
+
+## 字节序
+
+- 所有 int16_t 类型字段(x_move, y_move, yaw, pitch, feed)使用 **小端序**
+
+## 代码示例
+
+```cpp
+constexpr int FRAME_LENGTH = 15; // 总帧长度
+
+frame[0] = 0xBB; // SOF
+frame[1] = 0x77; // SOF
+frame[2] = x_move & 0xFF; // 低字节
+frame[3] = (x_move >> 8) & 0xFF; // 高字节
+// ... 其他字段
+frame[14] = 0xEE; // EOF
\ No newline at end of file
diff --git a/docs/Transmit/usb_device_setup.md b/docs/Transmit/usb_device_setup.md
new file mode 100644
index 0000000..a96ac44
--- /dev/null
+++ b/docs/Transmit/usb_device_setup.md
@@ -0,0 +1,87 @@
+# USB 串口设备权限设置指南
+
+## 文件说明
+
+| 文件 | 说明 |
+|------|------|
+| `99-usb-serial.rules` | udev 规则文件,固定设备名称和权限 |
+| `setup_usb_permissions.sh` | 自动化设置脚本 |
+
+## 操作步骤
+
+### 方法一:使用自动化脚本(推荐)
+
+```bash
+cd ~/code/amadeus_26
+./setup_usb_permissions.sh
+```
+
+### 方法二:手动设置
+
+1. **识别设备 VID/PID**
+
+```bash
+# 查看 CH340 设备信息
+udevadm info -a -n /dev/ttyUSB0 | grep -E "idVendor|idProduct"
+
+# 查看 IMU 设备信息
+udevadm info -a -n /dev/ttyUSB1 | grep -E "idVendor|idProduct"
+```
+
+2. **安装 udev 规则**
+
+```bash
+sudo cp 99-usb-serial.rules /etc/udev/rules.d/
+sudo udevadm control --reload-rules
+sudo udevadm trigger
+```
+
+3. **添加用户权限**
+
+```bash
+sudo usermod -aG dialout $USER
+```
+
+4. **生效**
+
+- 重新插拔 USB 设备,或重启系统
+- 注销并重新登录,使权限生效
+
+## 设备映射
+
+设置完成后,设备将以固定名称出现:
+
+| 设备 | 固定名称 | 原名称 |
+|------|----------|--------|
+| CH340 发送模块 | `/dev/ttyCH340` | `/dev/ttyUSB0` |
+| IMU 模块 | `/dev/ttyIMU` | `/dev/ttyUSB1` |
+
+## 验证
+
+```bash
+# 检查设备链接
+ls -la /dev/ttyCH340 /dev/ttyIMU
+
+# 检查权限
+ls -la /dev/ttyUSB*
+```
+
+## 使用
+
+修改 launch 文件中的默认设备路径:
+
+```python
+# launch/uart_transmitter.launch.py
+serial_port_arg = DeclareLaunchArgument(
+ 'serial_port',
+ default_value='/dev/ttyCH340', # 使用固定名称
+ description='CH340 串口设备路径'
+)
+```
+
+运行节点:
+
+```bash
+ros2 launch amadeus_26 uart_transmitter.launch.py
+# 或指定 IMU 设备
+ros2 launch amadeus_26 uart_transmitter.launch.py serial_port:=/dev/ttyIMU
\ No newline at end of file
diff --git a/launch/uart_transmitter.launch.py b/launch/uart_transmitter.launch.py
new file mode 100644
index 0000000..1e52044
--- /dev/null
+++ b/launch/uart_transmitter.launch.py
@@ -0,0 +1,102 @@
+from launch import LaunchDescription
+from launch_ros.actions import Node
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
+
+
+def generate_launch_description():
+ # 声明启动参数
+ serial_port_arg = DeclareLaunchArgument(
+ 'serial_port',
+ default_value='/dev/ttyCH340',
+ description='CH340 串口设备路径'
+ )
+
+ baudrate_arg = DeclareLaunchArgument(
+ 'baudrate',
+ default_value='115200',
+ description='串口波特率'
+ )
+
+ send_frequency_arg = DeclareLaunchArgument(
+ 'send_frequency',
+ default_value='10.0',
+ description='发送频率 (Hz)'
+ )
+
+ # 控制参数
+ x_move_arg = DeclareLaunchArgument(
+ 'x_move',
+ default_value='0',
+ description='平动左右 [-660, 660]'
+ )
+
+ y_move_arg = DeclareLaunchArgument(
+ 'y_move',
+ default_value='0',
+ description='平动前后 [-660, 660]'
+ )
+
+ yaw_arg = DeclareLaunchArgument(
+ 'yaw',
+ default_value='0',
+ description='云台偏航 [-660, 660]'
+ )
+
+ pitch_arg = DeclareLaunchArgument(
+ 'pitch',
+ default_value='0',
+ description='云台俯仰 [-660, 660]'
+ )
+
+ feed_arg = DeclareLaunchArgument(
+ 'feed',
+ default_value='0',
+ description='拨弹轮 [-660, 660]'
+ )
+
+ left_switch_arg = DeclareLaunchArgument(
+ 'left_switch',
+ default_value='3',
+ description='左拨杆 [1, 3]'
+ )
+
+ right_switch_arg = DeclareLaunchArgument(
+ 'right_switch',
+ default_value='3',
+ description='右拨杆 [1, 3]'
+ )
+
+ # 创建节点
+ uart_transmitter_node = Node(
+ package='amadeus_26',
+ executable='uart_transmitter_node',
+ name='uart_transmitter_node',
+ output='screen',
+ parameters=[{
+ 'serial_port': LaunchConfiguration('serial_port'),
+ 'baudrate': LaunchConfiguration('baudrate'),
+ 'send_frequency': LaunchConfiguration('send_frequency'),
+ 'x_move': LaunchConfiguration('x_move'),
+ 'y_move': LaunchConfiguration('y_move'),
+ 'yaw': LaunchConfiguration('yaw'),
+ 'pitch': LaunchConfiguration('pitch'),
+ 'feed': LaunchConfiguration('feed'),
+ 'left_switch': LaunchConfiguration('left_switch'),
+ 'right_switch': LaunchConfiguration('right_switch'),
+ }],
+ )
+
+ return LaunchDescription([
+ serial_port_arg,
+ baudrate_arg,
+ send_frequency_arg,
+ x_move_arg,
+ y_move_arg,
+ yaw_arg,
+ pitch_arg,
+ feed_arg,
+ left_switch_arg,
+ right_switch_arg,
+ uart_transmitter_node,
+ ])
\ No newline at end of file
diff --git a/camera_sdk/inc/CameraApi.h b/lib/camera_sdk/inc/CameraApi.h
similarity index 100%
rename from camera_sdk/inc/CameraApi.h
rename to lib/camera_sdk/inc/CameraApi.h
diff --git a/camera_sdk/inc/CameraDefine.h b/lib/camera_sdk/inc/CameraDefine.h
similarity index 100%
rename from camera_sdk/inc/CameraDefine.h
rename to lib/camera_sdk/inc/CameraDefine.h
diff --git a/camera_sdk/inc/CameraStatus.h b/lib/camera_sdk/inc/CameraStatus.h
similarity index 100%
rename from camera_sdk/inc/CameraStatus.h
rename to lib/camera_sdk/inc/CameraStatus.h
diff --git a/package.xml b/package.xml
new file mode 100644
index 0000000..9430c9b
--- /dev/null
+++ b/package.xml
@@ -0,0 +1,20 @@
+
+
+
+ amadeus_26
+ 0.0.1
+ Amadeus 26 transmission and control package
+ maintainer
+ MIT
+
+ ament_cmake
+
+ rclcpp
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+
+
+ ament_cmake
+
+
\ No newline at end of file
diff --git a/src/imu_receiver_node.cpp b/src/imu_receiver_node.cpp
new file mode 100644
index 0000000..49c39d8
--- /dev/null
+++ b/src/imu_receiver_node.cpp
@@ -0,0 +1,591 @@
+/**
+ * @file imu_receiver_node.cpp
+ * @brief IMU 数据接收节点 (FDI DETA10) - 高实时性版本
+ *
+ * 通过串口读取 FDI DETA10 IMU 模块的数据
+ * 波特率:921600
+ * 协议:FDILink
+ *
+ * 转发数据包:
+ * - MSG_AHRS (0x41): 航姿参考系统数据
+ * - MSG_BODY_ACCELERATION (0x62): 机体系加速度数据
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// FDI Link 帧定义
+constexpr uint8_t FRAME_START = 0xFC;
+constexpr uint8_t FRAME_END = 0xFD;
+
+// 数据包 ID
+constexpr uint8_t MSG_AHRS = 0x41; // 航姿参考系统数据
+constexpr uint8_t MSG_BODY_ACCELERATION = 0x62; // 机体系加速度数据
+
+// 默认串口设备
+constexpr const char *DEFAULT_SERIAL_PORT = "/dev/ttyIMU";
+constexpr int DEFAULT_BAUDRATE = 921600;
+
+// 串口读取缓冲区大小 (921600bps ≈ 115KB/s, 缓冲区需要足够大)
+constexpr size_t SERIAL_READ_BUF_SIZE = 4096;
+constexpr size_t MAX_FRAME_SIZE = 256;
+
+// CRC8 查找表
+static const uint8_t CRC8Table[] = {
+ 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31,
+ 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96,
+ 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128,
+ 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158,
+ 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56,
+ 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71,
+ 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167,
+ 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123,
+ 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81,
+ 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46,
+ 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19, 77, 206,
+ 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208,
+ 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202, 148, 118,
+ 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87, 9,
+ 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 233,
+ 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
+ 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107,
+ 53};
+
+// CRC16 查找表
+static const uint16_t CRC16Table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
+ 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
+ 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
+ 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
+ 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
+ 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
+ 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
+ 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
+ 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
+ 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
+ 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
+ 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
+ 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
+ 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
+ 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
+ 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
+ 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
+ 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
+ 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
+ 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
+ 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
+ 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
+ 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
+ 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
+ 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
+ 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
+
+class ImuReceiverNode : public rclcpp::Node {
+public:
+ ImuReceiverNode() : Node("imu_receiver_node") {
+ // 声明参数
+ this->declare_parameter("serial_port", DEFAULT_SERIAL_PORT);
+ this->declare_parameter("baudrate", DEFAULT_BAUDRATE);
+ this->declare_parameter("verbose", true); // 是否输出详细日志
+ this->declare_parameter("enable_crc", false); // 是否启用CRC校验(默认关闭)
+
+ // 获取参数
+ serial_port_ = this->get_parameter("serial_port").as_string();
+ baudrate_ = this->get_parameter("baudrate").as_int();
+ enable_crc_ = this->get_parameter("enable_crc").as_bool();
+
+ RCLCPP_INFO(this->get_logger(), "=================================");
+ RCLCPP_INFO(this->get_logger(), "IMU 数据接收节点启动 (高实时性版本)");
+ RCLCPP_INFO(this->get_logger(), "串口: %s", serial_port_.c_str());
+ RCLCPP_INFO(this->get_logger(), "波特率: %d", baudrate_);
+ RCLCPP_INFO(this->get_logger(), "CRC校验: %s",
+ enable_crc_ ? "启用" : "禁用");
+ RCLCPP_INFO(this->get_logger(), "=================================");
+
+ // 创建发布者
+ ahrs_pub_ = this->create_publisher("imu/ahrs", 10);
+ body_acc_pub_ = this->create_publisher(
+ "imu/body_acceleration", 10);
+ g_force_pub_ =
+ this->create_publisher("imu/g_force", 10);
+ connection_status_pub_ = this->create_publisher(
+ "imu/connection_status", 10);
+
+ // 初始化串口
+ if (!initSerial()) {
+ RCLCPP_ERROR(this->get_logger(), "串口初始化失败,节点退出");
+ rclcpp::shutdown();
+ return;
+ }
+
+ // 创建接收线程
+ receive_thread_ = std::thread(&ImuReceiverNode::receiveLoop, this);
+
+ // 创建状态发布定时器
+ status_timer_ = this->create_wall_timer(
+ std::chrono::seconds(1),
+ std::bind(&ImuReceiverNode::publishStatus, this));
+
+ RCLCPP_INFO(this->get_logger(), "IMU 节点初始化完成");
+ }
+
+ ~ImuReceiverNode() {
+ running_ = false;
+ if (receive_thread_.joinable()) {
+ receive_thread_.join();
+ }
+ if (serial_fd_ >= 0) {
+ close(serial_fd_);
+ }
+ RCLCPP_INFO(this->get_logger(), "IMU 串口已关闭");
+ }
+
+private:
+ bool initSerial() {
+ RCLCPP_INFO(this->get_logger(), "正在打开 IMU 串口 %s...",
+ serial_port_.c_str());
+
+ serial_fd_ = open(serial_port_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
+ if (serial_fd_ < 0) {
+ RCLCPP_ERROR(this->get_logger(), "无法打开串口 %s: %s",
+ serial_port_.c_str(), strerror(errno));
+ return false;
+ }
+
+ struct termios tty;
+ memset(&tty, 0, sizeof(tty));
+
+ if (tcgetattr(serial_fd_, &tty) != 0) {
+ RCLCPP_ERROR(this->get_logger(), "tcgetattr 错误: %s", strerror(errno));
+ close(serial_fd_);
+ serial_fd_ = -1;
+ return false;
+ }
+
+ speed_t baud = convertBaudrate(baudrate_);
+ cfsetospeed(&tty, baud);
+ cfsetispeed(&tty, baud);
+
+ tty.c_cflag &= ~PARENB;
+ tty.c_cflag &= ~CSTOPB;
+ tty.c_cflag &= ~CSIZE;
+ tty.c_cflag |= CS8;
+ tty.c_cflag |= CREAD | CLOCAL;
+ tty.c_cflag &= ~CRTSCTS;
+
+ tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tty.c_iflag &= ~(IXON | IXOFF | IXANY);
+ tty.c_oflag &= ~OPOST;
+
+ // 非阻塞读取,最小读取0字节,超时0
+ tty.c_cc[VMIN] = 0;
+ tty.c_cc[VTIME] = 0;
+
+ if (tcsetattr(serial_fd_, TCSANOW, &tty) != 0) {
+ RCLCPP_ERROR(this->get_logger(), "tcsetattr 错误: %s", strerror(errno));
+ close(serial_fd_);
+ serial_fd_ = -1;
+ return false;
+ }
+
+ tcflush(serial_fd_, TCIOFLUSH);
+
+ RCLCPP_INFO(this->get_logger(), "IMU 串口打开成功");
+ is_connected_ = true;
+ return true;
+ }
+
+ speed_t convertBaudrate(int baudrate) {
+ switch (baudrate) {
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 921600:
+ return B921600;
+ default:
+ return B921600;
+ }
+ }
+
+ uint8_t calculateCRC8(const uint8_t *data, uint8_t len) {
+ uint8_t crc8 = 0;
+ for (uint8_t i = 0; i < len; i++) {
+ crc8 = CRC8Table[crc8 ^ data[i]];
+ }
+ return crc8;
+ }
+
+ uint16_t calculateCRC16(const uint8_t *data, uint8_t len) {
+ uint16_t crc16 = 0;
+ for (uint8_t i = 0; i < len; i++) {
+ crc16 = CRC16Table[((crc16 >> 8) ^ data[i]) & 0xFF] ^ (crc16 << 8);
+ }
+ return crc16;
+ }
+
+ // 高实时性接收循环 - 批量读取,无阻塞
+ void receiveLoop() {
+ // 使用双缓冲区策略:一个用于接收,一个用于处理
+ std::vector rx_buffer;
+ rx_buffer.reserve(SERIAL_READ_BUF_SIZE * 2);
+
+ // 临时读取缓冲区
+ uint8_t read_buf[SERIAL_READ_BUF_SIZE];
+
+ // 帧解析状态
+ bool in_frame = false;
+ std::vector current_frame;
+ current_frame.reserve(MAX_FRAME_SIZE);
+
+ while (running_ && rclcpp::ok()) {
+ // 批量读取串口数据 - 非阻塞
+ ssize_t n = read(serial_fd_, read_buf, sizeof(read_buf));
+
+ if (n > 0) {
+ // 将新数据追加到缓冲区
+ rx_buffer.insert(rx_buffer.end(), read_buf, read_buf + n);
+
+ // 处理缓冲区中的数据
+ size_t i = 0;
+ while (i < rx_buffer.size()) {
+ uint8_t byte = rx_buffer[i];
+
+ if (!in_frame) {
+ // 查找帧头
+ if (byte == FRAME_START) {
+ // 检查是否有足够的字节来读取帧头信息
+ if (i + 5 <= rx_buffer.size()) {
+ // 读取帧头信息
+ uint8_t msg_id = rx_buffer[i + 1];
+ uint8_t data_len = rx_buffer[i + 2];
+ uint8_t seq = rx_buffer[i + 3];
+ uint8_t header_crc = rx_buffer[i + 4];
+
+ // 可选的CRC校验
+ bool header_valid = true;
+ if (enable_crc_) {
+ uint8_t header[4] = {FRAME_START, msg_id, data_len, seq};
+ header_valid = (calculateCRC8(header, 4) == header_crc);
+ }
+
+ if (header_valid) {
+ // 开始收集帧数据
+ in_frame = true;
+ current_frame.clear();
+ current_frame.push_back(FRAME_START);
+ current_frame.push_back(msg_id);
+ current_frame.push_back(data_len);
+ current_frame.push_back(seq);
+ current_frame.push_back(header_crc);
+
+ // 计算还需要读取多少字节
+ size_t total_frame_len =
+ 5 + 2 + data_len + 1; // 头+CRC16+数据+尾
+ size_t remaining = total_frame_len - 5; // 已经读取了5字节
+
+ // 检查缓冲区中是否有足够的数据
+ if (i + 5 + remaining <= rx_buffer.size()) {
+ // 数据足够,直接复制剩余部分
+ current_frame.insert(current_frame.end(),
+ rx_buffer.begin() + i + 5,
+ rx_buffer.begin() + i + 5 + remaining);
+
+ // 检查帧尾
+ if (current_frame.back() == FRAME_END) {
+ // 解析帧
+ processFrame(current_frame);
+ }
+ // 无论是否有帧尾,都结束当前帧处理
+ in_frame = false;
+ i += total_frame_len;
+ continue;
+ } else {
+ // 数据不够,需要等待更多数据
+ // 删除已处理的部分,保留当前帧的后续数据
+ rx_buffer.erase(rx_buffer.begin(), rx_buffer.begin() + i);
+ i = 5; // 保留当前帧头
+ break;
+ }
+ }
+ }
+ }
+ i++;
+ } else {
+ // 已经在帧中,继续收集数据
+ current_frame.push_back(byte);
+
+ // 检查是否到达帧尾
+ if (byte == FRAME_END) {
+ // 解析帧
+ processFrame(current_frame);
+ in_frame = false;
+ i++;
+ } else if (current_frame.size() >= MAX_FRAME_SIZE) {
+ // 帧太长,丢弃
+ in_frame = false;
+ i++;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ // 如果不在帧中且缓冲区太大,清理旧数据
+ if (!in_frame && rx_buffer.size() > SERIAL_READ_BUF_SIZE) {
+ // 保留最后一部分数据,可能包含不完整的帧头
+ if (rx_buffer.size() > 100) {
+ rx_buffer.erase(rx_buffer.begin(), rx_buffer.end() - 100);
+ }
+ }
+ } else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ // 读取错误
+ RCLCPP_ERROR(this->get_logger(), "串口读取错误: %s", strerror(errno));
+ }
+ // n == 0 或 EAGAIN: 没有数据可读,继续循环(无sleep,保证实时性)
+ }
+ }
+
+ // 处理完整的一帧数据
+ void processFrame(const std::vector &frame) {
+ // 最小帧长度检查:头(5) + CRC16(2) + 尾(1) = 8
+ if (frame.size() < 8)
+ return;
+
+ // 检查帧尾
+ if (frame.back() != FRAME_END)
+ return;
+
+ uint8_t msg_id = frame[1];
+ uint8_t data_len = frame[2];
+
+ // 验证数据长度
+ size_t expected_len = 5 + 2 + data_len + 1;
+ if (frame.size() != expected_len)
+ return;
+
+ // 可选的数据CRC校验
+ if (enable_crc_) {
+ uint16_t data_crc = (frame[5] << 8) | frame[6];
+ uint16_t calc_crc = calculateCRC16(frame.data() + 7, data_len);
+ if (data_crc != calc_crc)
+ return;
+ }
+
+ // 解析数据包
+ parsePacket(msg_id, frame.data() + 7, data_len);
+ }
+
+ void parsePacket(uint8_t msg_id, const uint8_t *data, uint8_t len) {
+ switch (msg_id) {
+ case MSG_AHRS:
+ ahrs_count_++;
+ parseAHRS(data, len);
+ break;
+ case MSG_BODY_ACCELERATION:
+ body_acc_count_++;
+ parseBodyAcceleration(data, len);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // 解析 AHRS 数据 (0x41)
+ void parseAHRS(const uint8_t *data, uint8_t len) {
+ if (len < 48)
+ return;
+
+ // 解析角速度 (rad/s)
+ float roll_speed = *reinterpret_cast(data);
+ float pitch_speed = *reinterpret_cast(data + 4);
+ float heading_speed = *reinterpret_cast(data + 8);
+
+ // 解析欧拉角 (rad)
+ float roll = *reinterpret_cast(data + 12);
+ float pitch = *reinterpret_cast(data + 16);
+ float heading = *reinterpret_cast(data + 20);
+
+ // 解析四元数
+ float q1 = *reinterpret_cast(data + 24);
+ float q2 = *reinterpret_cast(data + 28);
+ float q3 = *reinterpret_cast(data + 32);
+ float q4 = *reinterpret_cast(data + 36);
+
+ // 解析时间戳 (int64_t, us)
+ int64_t timestamp_us = *reinterpret_cast(data + 40);
+
+ // 将 IMU 时间戳转换为 ROS 时间
+ // 使用当前 ROS 时间作为基准,计算相对时间
+ rclcpp::Time current_time = this->now();
+ rclcpp::Time imu_time =
+ imu_base_time_ + rclcpp::Duration(0, timestamp_us * 1000);
+
+ // 如果是第一个数据包,初始化基准时间
+ if (!imu_time_initialized_) {
+ imu_base_time_ = current_time - rclcpp::Duration(0, timestamp_us * 1000);
+ imu_time = current_time;
+ imu_time_initialized_ = true;
+ }
+
+ // 保存最新的 IMU 时间戳,供 BodyAcceleration 使用
+ latest_imu_timestamp_ = imu_time;
+
+ // 发布 IMU 消息
+ sensor_msgs::msg::Imu imu_msg;
+ imu_msg.header.stamp = imu_time;
+ imu_msg.header.frame_id = "imu_link";
+
+ // 角速度 (转换为 ROS 坐标系: x=roll, y=pitch, z=heading)
+ imu_msg.angular_velocity.x = roll_speed;
+ imu_msg.angular_velocity.y = pitch_speed;
+ imu_msg.angular_velocity.z = heading_speed;
+
+ // 四元数 (FDI: Q1=w, Q2=x, Q3=y, Q4=z -> ROS: x,y,z,w)
+ imu_msg.orientation.x = q2;
+ imu_msg.orientation.y = q3;
+ imu_msg.orientation.z = q4;
+ imu_msg.orientation.w = q1;
+
+ // 线性加速度暂时设为0(从 MSG_BODY_ACCELERATION 获取)
+ imu_msg.linear_acceleration.x = 0;
+ imu_msg.linear_acceleration.y = 0;
+ imu_msg.linear_acceleration.z = 0;
+
+ ahrs_pub_->publish(imu_msg);
+
+ // 根据 verbose 参数输出日志
+ bool verbose = this->get_parameter("verbose").as_bool();
+ if (verbose) {
+ RCLCPP_INFO(
+ this->get_logger(),
+ "AHRS - Roll: %.3f, Pitch: %.3f, Heading: %.3f rad, Time: %ld us",
+ roll, pitch, heading, timestamp_us);
+ } else {
+ RCLCPP_DEBUG(this->get_logger(),
+ "AHRS - Roll: %.3f, Pitch: %.3f, Heading: %.3f rad", roll,
+ pitch, heading);
+ }
+ }
+
+ // 解析机体系加速度数据 (0x62)
+ void parseBodyAcceleration(const uint8_t *data, uint8_t len) {
+ if (len < 16)
+ return;
+
+ // 解析机体系加速度 (m/s²)
+ float acc_x = *reinterpret_cast(data);
+ float acc_y = *reinterpret_cast(data + 4);
+ float acc_z = *reinterpret_cast(data + 8);
+
+ // 解析重力加速度
+ float g_force = *reinterpret_cast(data + 12);
+
+ // 使用与 AHRS 相同的时间戳(如果可用),否则使用当前时间
+ rclcpp::Time msg_time = latest_imu_timestamp_;
+ if (msg_time.nanoseconds() == 0) {
+ msg_time = this->now();
+ }
+
+ // 发布机体系加速度
+ geometry_msgs::msg::Vector3 acc_msg;
+ acc_msg.x = acc_x;
+ acc_msg.y = acc_y;
+ acc_msg.z = acc_z;
+ body_acc_pub_->publish(acc_msg);
+
+ // 发布重力加速度
+ std_msgs::msg::Float32 g_msg;
+ g_msg.data = g_force;
+ g_force_pub_->publish(g_msg);
+
+ // 根据 verbose 参数输出日志
+ bool verbose = this->get_parameter("verbose").as_bool();
+ if (verbose) {
+ RCLCPP_INFO(this->get_logger(),
+ "BodyAcc - X: %.3f, Y: %.3f, Z: %.3f m/s2, G: %.3f", acc_x,
+ acc_y, acc_z, g_force);
+ } else {
+ RCLCPP_DEBUG(this->get_logger(),
+ "BodyAcc - X: %.3f, Y: %.3f, Z: %.3f m/s2, G: %.3f", acc_x,
+ acc_y, acc_z, g_force);
+ }
+ }
+
+ void publishStatus() {
+ auto msg = std_msgs::msg::Bool();
+ msg.data = is_connected_ && (serial_fd_ >= 0);
+ connection_status_pub_->publish(msg);
+
+ if (is_connected_) {
+ RCLCPP_INFO_THROTTLE(this->get_logger(), *this->get_clock(), 5000,
+ "IMU 接收正常 - AHRS: %zu, BodyAcc: %zu",
+ ahrs_count_.load(), body_acc_count_.load());
+ }
+ }
+
+ // 成员变量
+ std::string serial_port_;
+ int baudrate_;
+ bool enable_crc_ = false; // CRC校验开关
+
+ int serial_fd_ = -1;
+ bool is_connected_ = false;
+ std::atomic running_{true};
+ std::thread receive_thread_;
+
+ // 统计信息
+ std::atomic ahrs_count_;
+ std::atomic body_acc_count_;
+
+ // IMU 时间戳同步
+ bool imu_time_initialized_ = false;
+ rclcpp::Time imu_base_time_;
+ rclcpp::Time latest_imu_timestamp_;
+
+ rclcpp::Publisher::SharedPtr ahrs_pub_;
+ rclcpp::Publisher::SharedPtr body_acc_pub_;
+ rclcpp::Publisher::SharedPtr g_force_pub_;
+ rclcpp::Publisher::SharedPtr connection_status_pub_;
+ rclcpp::TimerBase::SharedPtr status_timer_;
+};
+
+int main(int argc, char *argv[]) {
+ rclcpp::init(argc, argv);
+
+ auto node = std::make_shared();
+
+ if (rclcpp::ok()) {
+ rclcpp::spin(node);
+ }
+
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/src/uart_transmitter_node.cpp b/src/uart_transmitter_node.cpp
new file mode 100644
index 0000000..9413ffd
--- /dev/null
+++ b/src/uart_transmitter_node.cpp
@@ -0,0 +1,400 @@
+/**
+ * @file uart_transmitter_node.cpp
+ * @brief UART 串口收发模块测试节点 (CH340)
+ *
+ * 默认波特率:115200
+ * 协议帧格式:
+ * | 0xBB | 0x77 | 平动左右 | 平动前后 | 云台偏航 | 云台俯仰 | 拨弹轮 | 拨杆 |
+ * CRC8 | 帧尾 | | 0xAA | 0x55 | 2 bytes | 2 bytes | 2 bytes | 2 bytes | 2
+ * bytes|1 byte| 1byte| 0xEE |
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// 帧定义
+constexpr uint8_t FRAME_HEADER_1 = 0xBB;
+constexpr uint8_t FRAME_HEADER_2 = 0x77;
+constexpr uint8_t FRAME_TAIL = 0xEE;
+constexpr int FRAME_LENGTH = 15;
+
+// 默认串口设备
+constexpr const char *DEFAULT_SERIAL_PORT = "/dev/ttyCH340";
+constexpr int DEFAULT_BAUDRATE = 115200;
+
+class UartTransmitterNode : public rclcpp::Node {
+public:
+ UartTransmitterNode() : Node("uart_transmitter_node") {
+ // 声明参数
+ this->declare_parameter("serial_port", DEFAULT_SERIAL_PORT);
+ this->declare_parameter("baudrate", DEFAULT_BAUDRATE);
+ this->declare_parameter("send_frequency", 50.0); // Hz
+
+ // 控制参数
+ this->declare_parameter("x_move", 0); // 平动左右 [-660, 660]
+ this->declare_parameter("y_move", 0); // 平动前后 [-660, 660]
+ this->declare_parameter("yaw", 0); // 云台偏航 [-660, 660]
+ this->declare_parameter("pitch", 0); // 云台俯仰 [-660, 660]
+ this->declare_parameter("feed", 0); // 拨弹轮 [-660, 660]
+ this->declare_parameter("left_switch", 0); // 左拨杆 [1, 3]
+ this->declare_parameter("right_switch", 0); // 右拨杆 [1, 3]
+
+ // 获取参数
+ serial_port_ = this->get_parameter("serial_port").as_string();
+ baudrate_ = this->get_parameter("baudrate").as_int();
+ send_frequency_ = this->get_parameter("send_frequency").as_double();
+
+ RCLCPP_INFO(this->get_logger(), "---------------------------------");
+ RCLCPP_INFO(this->get_logger(), "UART 收发节点启动");
+ RCLCPP_INFO(this->get_logger(), "串口: %s", serial_port_.c_str());
+ RCLCPP_INFO(this->get_logger(), "波特率: %d", baudrate_);
+ RCLCPP_INFO(this->get_logger(), "发送频率: %.1f Hz", send_frequency_);
+ RCLCPP_INFO(this->get_logger(), "---------------------------------");
+
+ // 初始化串口
+ if (!initSerial()) {
+ RCLCPP_ERROR(this->get_logger(), "串口初始化失败,节点退出");
+ rclcpp::shutdown();
+ return;
+ }
+
+ // 创建发布者
+ connection_status_pub_ = this->create_publisher(
+ "transmitter/connection_status", 10);
+
+ // 创建定时器 - 发送数据
+ auto send_period = std::chrono::duration(1.0 / send_frequency_);
+ send_timer_ = this->create_wall_timer(
+ std::chrono::duration_cast(send_period),
+ std::bind(&UartTransmitterNode::sendControlFrame, this));
+
+ // 创建定时器 - 发布连接状态
+ status_timer_ = this->create_wall_timer(
+ std::chrono::seconds(1),
+ std::bind(&UartTransmitterNode::publishStatus, this));
+
+ // 创建接收线程
+ receive_thread_ = std::thread(&UartTransmitterNode::receiveLoop, this);
+
+ RCLCPP_INFO(this->get_logger(), "UART 节点初始化完成");
+ }
+
+ ~UartTransmitterNode() {
+ running_ = false;
+ if (receive_thread_.joinable()) {
+ receive_thread_.join();
+ }
+ if (serial_fd_ >= 0) {
+ close(serial_fd_);
+ }
+ RCLCPP_INFO(this->get_logger(), "串口已关闭");
+ }
+
+private:
+ bool initSerial() {
+ RCLCPP_INFO(this->get_logger(), "正在打开串口 %s...", serial_port_.c_str());
+
+ // 打开串口
+ serial_fd_ = open(serial_port_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
+ if (serial_fd_ < 0) {
+ RCLCPP_ERROR(this->get_logger(), "无法打开串口 %s: %s",
+ serial_port_.c_str(), strerror(errno));
+ return false;
+ }
+
+ // 配置串口
+ struct termios tty;
+ memset(&tty, 0, sizeof(tty));
+
+ if (tcgetattr(serial_fd_, &tty) != 0) {
+ RCLCPP_ERROR(this->get_logger(), "tcgetattr 错误: %s", strerror(errno));
+ close(serial_fd_);
+ serial_fd_ = -1;
+ return false;
+ }
+
+ // 设置波特率
+ speed_t baud = convertBaudrate(baudrate_);
+ cfsetospeed(&tty, baud);
+ cfsetispeed(&tty, baud);
+
+ // 8N1
+ tty.c_cflag &= ~PARENB; // 无校验
+ tty.c_cflag &= ~CSTOPB; // 1位停止位
+ tty.c_cflag &= ~CSIZE;
+ tty.c_cflag |= CS8; // 8位数据
+ tty.c_cflag |= CREAD | CLOCAL; // 启用接收,忽略控制线
+
+ // 禁用硬件流控
+ tty.c_cflag &= ~CRTSCTS;
+
+ // 原始模式
+ tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tty.c_iflag &= ~(IXON | IXOFF | IXANY);
+ tty.c_oflag &= ~OPOST;
+
+ // 设置超时
+ tty.c_cc[VMIN] = 0;
+ tty.c_cc[VTIME] = 1; // 100ms 超时
+
+ if (tcsetattr(serial_fd_, TCSANOW, &tty) != 0) {
+ RCLCPP_ERROR(this->get_logger(), "tcsetattr 错误: %s", strerror(errno));
+ close(serial_fd_);
+ serial_fd_ = -1;
+ return false;
+ }
+
+ // 清空缓冲区
+ tcflush(serial_fd_, TCIOFLUSH);
+
+ RCLCPP_INFO(this->get_logger(), "串口打开成功");
+ is_connected_ = true;
+ return true;
+ }
+
+ speed_t convertBaudrate(int baudrate) {
+ switch (baudrate) {
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 921600:
+ return B921600;
+ default:
+ RCLCPP_WARN(this->get_logger(), "不支持的波特率 %d,使用 115200",
+ baudrate);
+ return B115200;
+ }
+ }
+
+ void sendControlFrame() {
+ if (serial_fd_ < 0) {
+ RCLCPP_WARN_THROTTLE(this->get_logger(), *this->get_clock(), 5000,
+ "串口未打开");
+ return;
+ }
+
+ // 获取控制参数
+ int16_t x_move =
+ static_cast(this->get_parameter("x_move").as_int());
+ int16_t y_move =
+ static_cast(this->get_parameter("y_move").as_int());
+ int16_t yaw = static_cast(this->get_parameter("yaw").as_int());
+ int16_t pitch = static_cast(this->get_parameter("pitch").as_int());
+ int16_t feed = static_cast(this->get_parameter("feed").as_int());
+ uint8_t left_switch =
+ static_cast(this->get_parameter("left_switch").as_int()) &
+ 0x0F;
+ uint8_t right_switch =
+ static_cast(this->get_parameter("right_switch").as_int()) &
+ 0x0F;
+
+ // 构建数据帧
+ uint8_t frame[FRAME_LENGTH];
+ int idx = 0;
+
+ // 帧头
+ frame[idx++] = FRAME_HEADER_1;
+ frame[idx++] = FRAME_HEADER_2;
+
+ // 平动左右 (2 bytes, int16, 小端序)
+ frame[idx++] = x_move & 0xFF;
+ frame[idx++] = (x_move >> 8) & 0xFF;
+
+ // 平动前后 (2 bytes, 小端序)
+ frame[idx++] = y_move & 0xFF;
+ frame[idx++] = (y_move >> 8) & 0xFF;
+
+ // 云台偏航 (2 bytes, 小端序)
+ frame[idx++] = yaw & 0xFF;
+ frame[idx++] = (yaw >> 8) & 0xFF;
+
+ // 云台俯仰 (2 bytes, 小端序)
+ frame[idx++] = pitch & 0xFF;
+ frame[idx++] = (pitch >> 8) & 0xFF;
+
+ // 拨弹轮 (2 bytes, 小端序)
+ frame[idx++] = feed & 0xFF;
+ frame[idx++] = (feed >> 8) & 0xFF;
+
+ // 拨杆 (1 byte: 高4位左拨杆,低4位右拨杆)
+ frame[idx++] = (left_switch << 4) | right_switch;
+
+ // CRC8 (除帧头和帧尾外的所有数据,不包括CRC本身)
+ // 当前idx=13, 数据长度=11 (从frame[2]到frame[12])
+ // uint8_t crc_value = calculateCRC8(frame + 2, 11);
+ frame[idx++] = 0xCC; // crc_value
+
+ // 帧尾
+ frame[idx++] = FRAME_TAIL;
+
+ // 调试输出:打印完整帧内容
+ std::string frame_hex;
+ char buf[4];
+ for (int i = 0; i < FRAME_LENGTH; i++) {
+ snprintf(buf, sizeof(buf), "%02X ", frame[i]);
+ frame_hex += buf;
+ }
+ RCLCPP_INFO(this->get_logger(), "[TX](%dB): %s", FRAME_LENGTH,
+ frame_hex.c_str());
+
+ // 发送数据
+ ssize_t written = write(serial_fd_, frame, FRAME_LENGTH);
+ if (written != FRAME_LENGTH) {
+ RCLCPP_WARN(this->get_logger(), "发送数据不完整: %zd/%d", written,
+ FRAME_LENGTH);
+ }
+ }
+
+ uint8_t calculateCRC8(const uint8_t *data, size_t len) {
+ uint8_t crc = 0xFF; // 初始值
+ for (size_t i = 0; i < len; i++) {
+ crc ^= data[i];
+ for (int j = 0; j < 8; j++) {
+ if (crc & 0x80) {
+ crc = (crc << 1) ^ 0x31; // CRC8-MAXIM 多项式
+ } else {
+ crc <<= 1;
+ }
+ }
+ }
+ return crc;
+ }
+
+ void receiveLoop() {
+ uint8_t buffer[256];
+ std::vector frame_buffer;
+ frame_buffer.reserve(FRAME_LENGTH);
+
+ while (running_ && rclcpp::ok()) {
+ if (serial_fd_ < 0) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ continue;
+ }
+
+ // 读取数据
+ ssize_t n = read(serial_fd_, buffer, sizeof(buffer));
+ if (n > 0) {
+ for (ssize_t i = 0; i < n; i++) {
+ frame_buffer.push_back(buffer[i]);
+
+ // 查找帧头
+ if (frame_buffer.size() >= 2 && frame_buffer[0] == FRAME_HEADER_1 &&
+ frame_buffer[1] == FRAME_HEADER_2) {
+
+ // 等待完整帧
+ if (frame_buffer.size() >= FRAME_LENGTH) {
+ // 检查帧尾
+ if (frame_buffer[FRAME_LENGTH - 1] == FRAME_TAIL) {
+ // 验证 CRC
+ uint8_t rx_crc = frame_buffer[FRAME_LENGTH - 2];
+ uint8_t calc_crc =
+ calculateCRC8(frame_buffer.data() + 2, FRAME_LENGTH - 4);
+
+ if (rx_crc == calc_crc) {
+ // 解析数据(小端序)
+ int16_t x_move = frame_buffer[2] | (frame_buffer[3] << 8);
+ int16_t y_move = frame_buffer[4] | (frame_buffer[5] << 8);
+ int16_t yaw = frame_buffer[6] | (frame_buffer[7] << 8);
+ int16_t pitch = frame_buffer[8] | (frame_buffer[9] << 8);
+ int16_t feed = frame_buffer[10] | (frame_buffer[11] << 8);
+ uint8_t key = frame_buffer[12];
+ uint8_t left_switch = (key >> 4) & 0x0F;
+ uint8_t right_switch = key & 0x0F;
+
+ // 输出详细日志
+ RCLCPP_INFO(this->get_logger(),
+ "[RX]: %02X %02X | x:%d y:%d yaw:%d "
+ "pitch:%d feed:%d | L:%d R:%d | CRC:%02X | %02X",
+ frame_buffer[0], frame_buffer[1], x_move, y_move,
+ yaw, pitch, feed, left_switch, right_switch,
+ rx_crc, frame_buffer[FRAME_LENGTH - 1]);
+ } else {
+ RCLCPP_WARN(
+ this->get_logger(),
+ "[CRC错误] 接收=%02X, 计算=%02X, 帧内容: %s", rx_crc,
+ calc_crc,
+ bytesToHex(frame_buffer.data(), FRAME_LENGTH).c_str());
+ }
+ } else {
+ RCLCPP_WARN(this->get_logger(),
+ "[帧尾错误] 期望=%02X, 实际=%02X", FRAME_TAIL,
+ frame_buffer[FRAME_LENGTH - 1]);
+ }
+
+ // 清空缓冲区,准备下一帧
+ frame_buffer.clear();
+ }
+ } else if (frame_buffer.size() > FRAME_LENGTH) {
+ // 缓冲区溢出,清空
+ frame_buffer.clear();
+ }
+ }
+ } else if (n < 0 && errno != EAGAIN) {
+ RCLCPP_ERROR(this->get_logger(), "读取错误: %s", strerror(errno));
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+
+ std::string bytesToHex(const uint8_t *data, size_t len) {
+ std::string result;
+ char buf[4];
+ for (size_t i = 0; i < len; i++) {
+ snprintf(buf, sizeof(buf), "%02X ", data[i]);
+ result += buf;
+ }
+ return result;
+ }
+
+ void publishStatus() {
+ auto msg = std_msgs::msg::Bool();
+ msg.data = is_connected_ && (serial_fd_ >= 0);
+ connection_status_pub_->publish(msg);
+ }
+
+ // 成员变量
+ std::string serial_port_;
+ int baudrate_;
+ double send_frequency_;
+
+ int serial_fd_ = -1;
+ bool is_connected_ = false;
+ bool running_ = true;
+ std::thread receive_thread_;
+
+ rclcpp::Publisher::SharedPtr connection_status_pub_;
+ rclcpp::TimerBase::SharedPtr send_timer_;
+ rclcpp::TimerBase::SharedPtr status_timer_;
+};
+
+int main(int argc, char *argv[]) {
+ rclcpp::init(argc, argv);
+
+ auto node = std::make_shared();
+
+ if (rclcpp::ok()) {
+ rclcpp::spin(node);
+ }
+
+ rclcpp::shutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/tool/99-usb-serial.rules b/tool/99-usb-serial.rules
new file mode 100644
index 0000000..a798e46
--- /dev/null
+++ b/tool/99-usb-serial.rules
@@ -0,0 +1,5 @@
+# CH340 串口模块 (ttyUSB0 -> ttyCH340)
+SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyCH340", MODE="0666", GROUP="dialout"
+
+# IMU 串口模块 (ttyUSB1 -> ttyIMU)
+SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="ttyIMU", MODE="0666", GROUP="dialout"
\ No newline at end of file
diff --git a/tool/setup_usb_permissions.sh b/tool/setup_usb_permissions.sh
new file mode 100755
index 0000000..fdac62e
--- /dev/null
+++ b/tool/setup_usb_permissions.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+# USB 串口设备权限和固定名称设置脚本
+
+set -e
+
+echo "=================================="
+echo "USB 串口设备权限设置"
+echo "=================================="
+
+# 1. 检查当前设备
+echo ""
+echo "当前连接的 USB 串口设备:"
+ls -la /dev/ttyUSB* 2>/dev/null || echo "未发现 ttyUSB 设备"
+
+echo ""
+echo "设备详细信息:"
+for dev in /dev/ttyUSB*; do
+ if [ -e "$dev" ]; then
+ echo "--- $dev ---"
+ udevadm info -a -n "$dev" | grep -E "(idVendor|idProduct|serial)" | head -5
+ fi
+done
+
+# 2. 安装规则文件
+echo ""
+echo "安装 udev 规则"
+sudo cp 99-usb-serial.rules /etc/udev/rules.d/
+
+# 3. 重新加载 udev 规则
+echo "重新加载 udev 规则"
+sudo udevadm control --reload-rules
+sudo udevadm trigger
+
+# 4. 添加用户到 dialout 组
+echo "将当前用户添加到 dialout 组"
+sudo usermod -aG dialout $USER
+
+echo "设置完成,请重新插拔设备"
\ No newline at end of file
diff --git a/transmitter_sdk/example.c b/transmitter_sdk/example.c
deleted file mode 100644
index e69de29..0000000
diff --git a/transmitter_sdk/inc/pub_user.h b/transmitter_sdk/inc/pub_user.h
deleted file mode 100755
index 27fc139..0000000
--- a/transmitter_sdk/inc/pub_user.h
+++ /dev/null
@@ -1,149 +0,0 @@
-//
-// Created by 93094 on 2025/12/23.
-//
-
-#ifndef DM_DEVICE_PUB_USER_H
-#define DM_DEVICE_PUB_USER_H
-
-#define DEVICE_EXPORTS
-
-#ifdef _WIN32
- #ifdef DEVICE_EXPORTS
- #define DEVICE_API __declspec(dllexport)
- #else
- #define DEVICE_API __declspec(dllimport)
- #endif
-#else
- #define DEVICE_API __attribute__((visibility("default")))
-#endif
-
-#ifdef __cplusplus
-
-#include
-
-extern "C"
-{
- typedef struct damiao_handle hDamiaoUsb;
- typedef struct device_handle hDevice;
-
-#pragma pack(push,1)
- typedef struct
- {
- uint32_t can_id:29; //can id
- uint32_t esi:1; //错误状态指示 一般不用
- uint32_t ext:1; //拓展帧 1-拓展帧 0-标准帧
- uint32_t rtr:1; //远程帧 1-远程帧 0-数据帧
- uint8_t canfd:1; //canfd 1-canfd帧 0-can2.0帧
- uint8_t brs:1; //波特率切换 1-切换 0-不切换
- uint8_t id_inc:1; //ID自增 1-自增 0-不自增
- uint8_t data_inc:1; //数据自增 1-自增 0-不自增
- uint8_t dlc:4; //数据长度
- uint8_t channel; //通道号
- uint16_t reserved; //预留字节
- uint16_t step_id; //步进ID
- uint32_t stop_id; //停止ID
- uint32_t interval; //发送间隔
- int32_t send_times; //发送次数
-
- }usb_tx_frame_head_t;
-
- typedef struct
- {
- usb_tx_frame_head_t head;
- uint8_t payload[64];
-
- }usb_tx_frame_t;
-
-
- typedef struct
- {
- uint32_t can_id:29; //can id
- uint32_t esi:1; //错误状态指示 一般不用
- uint32_t ext:1; //类型:标准/拓展
- uint32_t rtr:1; //类型:数据/远程
- uint64_t time_stamp; //时间戳
- uint8_t channel; //发送通道
- uint8_t canfd:1; //类型:2.0/fd
- uint8_t dir:1; //方向:rx/tx
- uint8_t brs:1; //BRS
- uint8_t ack:1; //应答标志
- uint8_t dlc:4; //长度
- uint16_t reserved; //预留字节
- }usb_rx_frame_head_t;
-
- typedef struct
- {
- usb_rx_frame_head_t head;
- uint8_t payload[64];
-
- }usb_rx_frame_t ;
-
- typedef struct
- {
- int can_baudrate;
- int canfd_baudrate;
- float can_sp;
- float canfd_sp;
- }device_baud_t;
-
- typedef enum
- {
- DEV_None=-1,
- DEV_USB2CANFD=0,
- DEV_USB2CANFD_DUAL,
- DEV_ECAT2CANFD
- }device_def_t;
-
- typedef struct
- {
- uint8_t channel;
- uint8_t can_fd;
- uint8_t can_seg1;
- uint8_t can_seg2;
- uint8_t can_sjw;
- uint8_t can_prescaler;
- uint8_t canfd_seg1;
- uint8_t canfd_seg2;
- uint8_t canfd_sjw;
- uint8_t canfd_prescaler;
- }dmcan_ch_can_config_t;
-
-
-#pragma pack(pop)
-
-
- typedef void (*dev_rec_callback)(usb_rx_frame_t* rec_frame);
- typedef void (*dev_sent_callback)(usb_rx_frame_t* sent_frame);
- typedef void (*dev_err_callback)(usb_rx_frame_t* err_frame);
-
-
- DEVICE_API damiao_handle* damiao_handle_create(device_def_t type);
- DEVICE_API void damiao_handle_destroy(damiao_handle* handle);
- DEVICE_API void damiao_print_version(damiao_handle* handle);
- DEVICE_API void damiao_get_sdk_version(damiao_handle* handle, char* version_buf, size_t buf_size);
- DEVICE_API int damiao_handle_find_devices(damiao_handle* handle);
- DEVICE_API void damiao_handle_get_devices(damiao_handle* handle,device_handle** dev_list, int* device_count);
- DEVICE_API void device_get_version(device_handle* dev, char* version_buf, size_t buf_size);
- DEVICE_API void device_get_pid_vid(device_handle* dev, int* pid, int* vid);
- DEVICE_API void device_get_serial_number(device_handle* dev, char* serial_buf, size_t buf_size);
- DEVICE_API void device_get_type(device_handle* dev, device_def_t* type);
- DEVICE_API bool device_open(device_handle* dev);
- DEVICE_API bool device_close(device_handle* dev);
- DEVICE_API bool device_save_config(device_handle* dev);
- DEVICE_API bool device_open_channel(device_handle* dev, uint8_t channel);
- DEVICE_API bool device_close_channel(device_handle* dev, uint8_t channel);
- DEVICE_API bool device_channel_get_baudrate(device_handle*dev ,uint8_t channel,device_baud_t* baud);
- DEVICE_API bool device_channel_set_baud(device_handle*dev ,uint8_t channel,bool canfd,int bitrate,int dbitrate);
- DEVICE_API bool device_channel_set_baud_with_sp(device_handle*dev ,uint8_t channel,bool canfd,int bitrate,int dbitrate,float can_sp,float canfd_sp);
- DEVICE_API void device_hook_to_rec(device_handle*dev,dev_rec_callback callback);
- DEVICE_API void device_hook_to_sent(device_handle*dev,dev_sent_callback callback);
- DEVICE_API void device_hook_to_err(device_handle*dev,dev_err_callback callback);
- DEVICE_API void device_channel_send(device_handle*dev,usb_tx_frame_t frame);
- DEVICE_API void device_channel_send_fast(device_handle*dev,uint8_t ch,uint32_t can_id,int32_t cnt,bool ext,bool canfd,bool brs,uint8_t len,uint8_t* payload);
- DEVICE_API void device_channel_send_advanced(device_handle*dev,uint8_t ch,uint32_t can_id,uint16_t step_id,uint32_t stop_id,int32_t cnt,bool id_inc,bool data_inc,bool ext,bool canfd,bool brs,uint8_t len,uint8_t* payload);
-}
-
-
-#endif
-
-#endif //DM_DEVICE_PUB_USER_H
\ No newline at end of file
diff --git a/transmitter_sdk/lib/linux/arm64/libdm_device.so b/transmitter_sdk/lib/linux/arm64/libdm_device.so
deleted file mode 100755
index ed22cbb..0000000
Binary files a/transmitter_sdk/lib/linux/arm64/libdm_device.so and /dev/null differ
diff --git a/transmitter_sdk/lib/linux/x64/libdm_device.so b/transmitter_sdk/lib/linux/x64/libdm_device.so
deleted file mode 100755
index 3727af8..0000000
Binary files a/transmitter_sdk/lib/linux/x64/libdm_device.so and /dev/null differ