将src目录作为普通文件而非子模块添加

This commit is contained in:
2025-09-11 15:44:41 +08:00
parent 690eef76fe
commit 52ae8d29c8
14 changed files with 1349 additions and 1 deletions

1
src

Submodule src deleted from ec1c1e29cd

1
src/CMakeLists.txt Symbolic link
View File

@@ -0,0 +1 @@
/opt/ros/noetic/share/catkin/cmake/toplevel.cmake

146
src/README.md Normal file
View File

@@ -0,0 +1,146 @@
# TR360 ROS 包
基于ROS Noetic的TR360雷达点云处理包支持TR360-3-60避障雷达和TR360-4-60地形雷达。
## 功能特性
- ✅ UDP数据接收和解析
- ✅ 点云数据转换(极坐标→直角坐标)
- ✅ PointCloud2话题发布
- ✅ RViz可视化支持
- ✅ 动态参数配置
- ✅ 多雷达类型支持
- ✅ 网络参数可配置
- ✅ 测试数据模拟
## 系统要求
- Ubuntu 20.04
- ROS Noetic
- PCL 1.10
## 安装和编译
### 自动编译
```bash
cd tr360_ros
./build.sh
```
### 手动编译
```bash
# 设置ROS环境
source /opt/ros/noetic/setup.bash
# 创建工作空间(如果不存在)
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
# 复制包到工作空间
cp -r /path/to/tr360_ros ~/catkin_ws/src/
# 编译
cd ~/catkin_ws
catkin_make
# 设置环境
source devel/setup.bash
```
## 使用方法
### 1. 启动主节点(需要真实雷达)
```bash
roslaunch tr360_ros tr360.launch
```
### 2. 启动测试节点(无雷达时使用)
```bash
rosrun tr360_ros test_publisher
```
### 3. 可视化
```bash
rviz -d $(rospack find tr360_ros)/config/tr360.rviz
```
## 参数配置
### 启动参数
`launch/tr360.launch`中配置:
- `radar_type`: 雷达类型 (`TR360-3-60`/`TR360-4-60`)
- `radar_ip`: 雷达设备IP
- `radar_port`: 雷达端口 (默认: 10123)
- `local_ip`: 本地接收IP
- `local_port`: 本地端口 (默认: 10193/20193)
### 动态参数
运行时可通过`rqt_reconfigure`调整:
- 距离范围限制
- 角度范围限制
- 强度范围限制
- 坐标显示范围
- 发布频率
## 网络配置
### TR360-3-60 (避障雷达)
- 雷达IP: `192.168.1.80`
- 雷达端口: `10123`
- 本地IP: `192.168.1.193`
- 本地端口: `10193`
### TR360-4-60 (地形雷达)
- 雷达IP: `192.168.0.80`
- 雷达端口: `10123`
- 本地IP: `192.168.0.193`
- 本地端口: `20193`
## 数据结构
### 点云格式
每个点包含:
- 方位角 (9 bits)
- 距离 (12 bits)
- 俯仰角 (11 bits)
- RCS值 (8 bits)
### 坐标转换
极坐标 → 直角坐标:
```
{x, y, z, v} = {r*cos(e)*cos(a), r*cos(e)*sin(a), r*sin(e), v}
```
## 话题
### 多雷达支持
支持同时处理多个TR360雷达数据根据雷达类型和数量自动生成话题名称
#### TR360-3-60 双雷达配置 (max_radars=2)
- `/TR360-3-30-up` (`sensor_msgs/PointCloud2`) - 上方雷达点云数据
- `/TR360-3-60-front` (`sensor_msgs/PointCloud2`) - 前方雷达点云数据
#### TR360-3-60 单雷达配置 (max_radars=1)
- `/TR360-4-60-terrain` (`sensor_msgs/PointCloud2`) - 地形雷达点云数据
#### 默认配置
- `/tr360_points_1` (`sensor_msgs/PointCloud2`) - 雷达1的点云数据
- `/tr360_points_2` (`sensor_msgs/PointCloud2`) - 雷达2的点云数据
- `/tr360_points_3` (`sensor_msgs/PointCloud2`) - 雷达3的点云数据
- `/tr360_points_4` (`sensor_msgs/PointCloud2`) - 雷达4的点云数据
### 坐标系
每个雷达使用独立的坐标系:
- `tr360_radar_1` - 雷达1坐标系
- `tr360_radar_2` - 雷达2坐标系
- `tr360_radar_3` - 雷达3坐标系
- `tr360_radar_4` - 雷达4坐标系
## 版本控制
使用Git进行版本管理采用分步测试开发模式。
## 许可证
BSD 3-Clause License

41
src/build.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
# TR360 ROS包编译脚本
echo "正在编译TR360 ROS包..."
# 设置ROS环境
source /opt/ros/noetic/setup.bash
# 创建编译目录
BUILD_DIR="$HOME/catkin_ws"
if [ ! -d "$BUILD_DIR" ]; then
echo "创建catkin工作空间..."
mkdir -p "$BUILD_DIR/src"
cd "$BUILD_DIR"
catkin_make
fi
# 复制包到工作空间
PACKAGE_DIR="$BUILD_DIR/src/tr360_ros"
if [ -d "$PACKAGE_DIR" ]; then
echo "删除旧版包..."
rm -rf "$PACKAGE_DIR"
fi
echo "复制包到工作空间..."
cp -r "$(dirname "$0")" "$BUILD_DIR/src/"
# 编译包
cd "$BUILD_DIR"
echo "开始编译..."
catkin_make
# 设置环境变量
echo "source $BUILD_DIR/devel/setup.bash" >> ~/.bashrc
source "$BUILD_DIR/devel/setup.bash"
echo "编译完成!"
echo "使用方法:"
echo "1. 启动主节点: roslaunch tr360_ros tr360.launch"
echo "2. 启动测试节点: rosrun tr360_ros test_publisher"
echo "3. 可视化: rviz -d $(rospack find tr360_ros)/config/tr360.rviz"

View File

@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.0.2)
project(tr360_ros)
find_package(catkin REQUIRED COMPONENTS
roscpp
sensor_msgs
pcl_ros
pcl_conversions
dynamic_reconfigure
)
# 添加动态参数配置生成
find_package(catkin REQUIRED)
generate_dynamic_reconfigure_options(
cfg/TR360Config.cfg
)
catkin_package(
INCLUDE_DIRS include
LIBRARIES tr360_ros
CATKIN_DEPENDS roscpp sensor_msgs pcl_ros pcl_conversions
)
include_directories(
include
${catkin_INCLUDE_DIRS}
${PCL_INCLUDE_DIRS}
)
add_executable(tr360_node src/tr360_node.cpp)
target_link_libraries(tr360_node
${catkin_LIBRARIES}
${PCL_LIBRARIES}
)
# 添加网络库链接
target_link_libraries(tr360_node ${Boost_LIBRARIES})
# 测试发布器
add_executable(test_publisher src/test_publisher.cpp)
target_link_libraries(test_publisher
${catkin_LIBRARIES}
${PCL_LIBRARIES}
)
install(TARGETS test_publisher
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
install(TARGETS tr360_node
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
)
install(DIRECTORY config/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config
)

146
src/tr360_ros/README.md Normal file
View File

@@ -0,0 +1,146 @@
# TR360 ROS 包
基于ROS Noetic的TR360雷达点云处理包支持TR360-3-60避障雷达和TR360-4-60地形雷达。
## 功能特性
- ✅ UDP数据接收和解析
- ✅ 点云数据转换(极坐标→直角坐标)
- ✅ PointCloud2话题发布
- ✅ RViz可视化支持
- ✅ 动态参数配置
- ✅ 多雷达类型支持
- ✅ 网络参数可配置
- ✅ 测试数据模拟
## 系统要求
- Ubuntu 20.04
- ROS Noetic
- PCL 1.10
## 安装和编译
### 自动编译
```bash
cd tr360_ros
./build.sh
```
### 手动编译
```bash
# 设置ROS环境
source /opt/ros/noetic/setup.bash
# 创建工作空间(如果不存在)
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make
# 复制包到工作空间
cp -r /path/to/tr360_ros ~/catkin_ws/src/
# 编译
cd ~/catkin_ws
catkin_make
# 设置环境
source devel/setup.bash
```
## 使用方法
### 1. 启动主节点(需要真实雷达)
```bash
roslaunch tr360_ros tr360.launch
```
### 2. 启动测试节点(无雷达时使用)
```bash
rosrun tr360_ros test_publisher
```
### 3. 可视化
```bash
rviz -d $(rospack find tr360_ros)/config/tr360.rviz
```
## 参数配置
### 启动参数
`launch/tr360.launch`中配置:
- `radar_type`: 雷达类型 (`TR360-3-60`/`TR360-4-60`)
- `radar_ip`: 雷达设备IP
- `radar_port`: 雷达端口 (默认: 10123)
- `local_ip`: 本地接收IP
- `local_port`: 本地端口 (默认: 10193/20193)
### 动态参数
运行时可通过`rqt_reconfigure`调整:
- 距离范围限制
- 角度范围限制
- 强度范围限制
- 坐标显示范围
- 发布频率
## 网络配置
### TR360-3-60 (避障雷达)
- 雷达IP: `192.168.1.80`
- 雷达端口: `10123`
- 本地IP: `192.168.1.193`
- 本地端口: `10193`
### TR360-4-60 (地形雷达)
- 雷达IP: `192.168.0.80`
- 雷达端口: `10123`
- 本地IP: `192.168.0.193`
- 本地端口: `20193`
## 数据结构
### 点云格式
每个点包含:
- 方位角 (9 bits)
- 距离 (12 bits)
- 俯仰角 (11 bits)
- RCS值 (8 bits)
### 坐标转换
极坐标 → 直角坐标:
```
{x, y, z, v} = {r*cos(e)*cos(a), r*cos(e)*sin(a), r*sin(e), v}
```
## 话题
### 多雷达支持
支持同时处理多个TR360雷达数据根据雷达类型和数量自动生成话题名称
#### TR360-3-60 双雷达配置 (max_radars=2)
- `/TR360-3-30-up` (`sensor_msgs/PointCloud2`) - 上方雷达点云数据
- `/TR360-3-60-front` (`sensor_msgs/PointCloud2`) - 前方雷达点云数据
#### TR360-3-60 单雷达配置 (max_radars=1)
- `/TR360-4-60-terrain` (`sensor_msgs/PointCloud2`) - 地形雷达点云数据
#### 默认配置
- `/tr360_points_1` (`sensor_msgs/PointCloud2`) - 雷达1的点云数据
- `/tr360_points_2` (`sensor_msgs/PointCloud2`) - 雷达2的点云数据
- `/tr360_points_3` (`sensor_msgs/PointCloud2`) - 雷达3的点云数据
- `/tr360_points_4` (`sensor_msgs/PointCloud2`) - 雷达4的点云数据
### 坐标系
每个雷达使用独立的坐标系:
- `tr360_radar_1` - 雷达1坐标系
- `tr360_radar_2` - 雷达2坐标系
- `tr360_radar_3` - 雷达3坐标系
- `tr360_radar_4` - 雷达4坐标系
## 版本控制
使用Git进行版本管理采用分步测试开发模式。
## 许可证
BSD 3-Clause License

41
src/tr360_ros/build.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
# TR360 ROS包编译脚本
echo "正在编译TR360 ROS包..."
# 设置ROS环境
source /opt/ros/noetic/setup.bash
# 创建编译目录
BUILD_DIR="$HOME/catkin_ws"
if [ ! -d "$BUILD_DIR" ]; then
echo "创建catkin工作空间..."
mkdir -p "$BUILD_DIR/src"
cd "$BUILD_DIR"
catkin_make
fi
# 复制包到工作空间
PACKAGE_DIR="$BUILD_DIR/src/tr360_ros"
if [ -d "$PACKAGE_DIR" ]; then
echo "删除旧版包..."
rm -rf "$PACKAGE_DIR"
fi
echo "复制包到工作空间..."
cp -r "$(dirname "$0")" "$BUILD_DIR/src/"
# 编译包
cd "$BUILD_DIR"
echo "开始编译..."
catkin_make
# 设置环境变量
echo "source $BUILD_DIR/devel/setup.bash" >> ~/.bashrc
source "$BUILD_DIR/devel/setup.bash"
echo "编译完成!"
echo "使用方法:"
echo "1. 启动主节点: roslaunch tr360_ros tr360.launch"
echo "2. 启动测试节点: rosrun tr360_ros test_publisher"
echo "3. 可视化: rviz -d $(rospack find tr360_ros)/config/tr360.rviz"

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python
PACKAGE = "tr360_ros"
from dynamic_reconfigure.parameter_generator_catkin import *
# 距离参数
gen = ParameterGenerator()
# 距离限制参数
gen.add("r_lo", double_t, 0, "距离下限(m)", 0.0, 0.0, 100.0)
gen.add("r_hi", double_t, 0, "距离上限(m)", 100.0, 0.0, 100.0)
# 方位角参数
gen.add("az_lo", double_t, 0, "方位角下限(度)", -180.0, -180.0, 180.0)
gen.add("az_hi", double_t, 0, "方位角上限(度)", 180.0, -180.0, 180.0)
# 俯仰角参数
gen.add("el_lo", double_t, 0, "俯仰角下限(度)", -90.0, -90.0, 90.0)
gen.add("el_hi", double_t, 0, "俯仰角上限(度)", 90.0, -90.0, 90.0)
# 强度参数
gen.add("intensity_lo", double_t, 0, "强度下限", 0.0, 0.0, 100.0)
gen.add("intensity_hi", double_t, 0, "强度上限", 100.0, 0.0, 100.0)
# 坐标显示范围
gen.add("x_lo", double_t, 0, "X轴显示下限(m)", -50.0, -100.0, 100.0)
gen.add("x_hi", double_t, 0, "X轴显示上限(m)", 50.0, -100.0, 100.0)
gen.add("y_lo", double_t, 0, "Y轴显示下限(m)", -50.0, -100.0, 100.0)
gen.add("y_hi", double_t, 0, "Y轴显示上限(m)", 50.0, -100.0, 100.0)
gen.add("z_lo", double_t, 0, "Z轴显示下限(m)", -50.0, -100.0, 100.0)
gen.add("z_hi", double_t, 0, "Z轴显示上限(m)", 50.0, -100.0, 100.0)
# 雷达类型选择
gen.add("radar_type", str_t, 0, "雷达类型", "TR360-3-60", edit_method={"enum_description": ["TR360-3-60: 避障雷达", "TR360-4-60: 地形雷达"]})
# 网络配置
gen.add("radar_ip", str_t, 0, "雷达IP地址", "192.168.1.80")
gen.add("radar_port", int_t, 0, "雷达端口", 10123, 1024, 65535)
gen.add("local_ip", str_t, 0, "本地IP地址", "192.168.1.193")
gen.add("local_port", int_t, 0, "本地端口", 10193, 1024, 65535)
# 点云处理参数
gen.add("max_points", int_t, 0, "最大点数", 20000, 1000, 100000)
gen.add("publish_rate", double_t, 0, "发布频率(Hz)", 10.0, 1.0, 100.0)
exit(gen.generate(PACKAGE, "tr360_ros", "TR360Config"))

View File

@@ -0,0 +1,177 @@
Panels:
- Class: rviz/Displays
Help Height: 78
Name: Displays
Property Tree Widget:
Expanded:
- /Global Options1
- /Status1
Splitter Ratio: 0.5
Tree Height: 323
- Class: rviz/Selection
Name: Selection
- Class: rviz/Tool Properties
Expanded:
- /2D Pose Estimate1
- /2D Nav Goal1
- /Publish Point1
Name: Tool Properties
Splitter Ratio: 0.5886790156364441
- Class:极 rviz/Views
Name: Views
Splitter Ratio: 0.5
- Class: rviz/Time
Name: Time
Splitter Ratio: 0.5
Visualization Manager:
Class: ""
Displays:
# 雷达1点云显示
- Alpha: 0.5
Class: rviz/PointCloud2
Color: 1 0 0
Color Transformer: Intensity
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255
Min Color: 0
Name: TR360 Radar 1
Position Transformer: XYZ
Selectable: true
Size (Pixels): 3
Style: Points
Topic: /tr360_points_1
Unreliable: false
Use Fixed Frame: true
Use rainbow: true
Value: true
# 雷达2点云显示
- Alpha: 0.5
Class极: rviz/PointCloud2
Color: 0 1 0
Color Transformer: Intensity
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255
Min Color: 0
Name: TR360 Radar 2
Position Transformer: XYZ
Selectable: true
Size (Pixels): 3
Style: Points
Topic: /tr360_points_2
Unreliable: false
Use Fixed Frame: true
Use rainbow: true
Value: true
# 雷达3点云显示
- Alpha: 0.5
Class: rviz/PointCloud2
Color: 0 0 1
Color Transformer: Intensity
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255
Min Color: 0
Name: TR360 Radar 3
Position Transformer: XYZ
Selectable: true
Size (Pixels): 3
Style: Points
Topic: /tr360_points_3
Unreliable: false
Use Fixed Frame: true
Use rainbow: true
Value: true
# 雷达4点云显示
- Alpha: 0.5
Class: rviz/PointCloud2
Color: 1 1 0
Color Transformer: Intensity
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255
Min Color: 0
Name: TR360 Radar 4
Position Transformer: XYZ
Selectable: true
Size (Pixels): 3
Style: Points
Topic: /tr360_points_4
Unreliable: false
Use Fixed Frame: true
Use rainbow: true
Value: true
- Class: rviz/Grid
Cell Size: 1
Color: 0.9411764740943909 0.9411764740943909 0.9411764740943909
Enabled: true
Line Style: Lines
Line Width: 0.03
Name: Grid
Normal Cell Count: 0
Offset:
X: 0
Y: 0
Z: 0
Plane: XY
Plane Cell Count: 10
Reference Frame: ""
Value: true
Enabled: true
Global Options:
Background Color: 48 48 48
Fixed Frame: tr360_radar_1
Frame Rate: 30
Name: root
Tools:
- Class: rviz/Interact
Hide Inactive Objects: false
- Class: rviz/MoveCamera
- Class: rviz/Select
- Class: rviz/FocusCamera
- Class: rviz/Measure
- Class: rviz/2D Pose Estimate
- Class: rviz/2D Nav Goal
- Class: rviz/Publish Point
Value: rviz/Interact
Views:
Current:
Class: rviz/Orbit
Distance: 10
Enable Stereo Rendering: true
Focal Point:
X: 0
Y: 0
Z: 0
Focal Shape Fixed Size: true
Focal Shape Size: 0.05
Name: Current View
Near Clip Distance: 0.01
Pitch: 0.7853981852531433
Target Frame: ""
Value: Orbit (rviz)
Yaw: 0.7853981852531433
Saved: ~
Window Geometry:
Displays:
collapsed: false
Height: 768
Hide Left Dock: false
Hide Right Dock: false
QMainWindow State: 000000ff00000000fd0000000000000000000001d6000003c4fcfd0000000bf000000000000000000000432000001590000000643ffffff0000001000000032000001d6fcfd00000002df0000020f00000001000000000000000000000000fc0200000002fb000000100064006900730070006c00610079007300000001df0000020f00000001000000000000000000000000fc0100000001fb0000000a00760069006500770073000000014300000159fcfd0000000a000000000000000000000432000001590000000000000000000000000000000000000200000001000000005f0000012dfc0200000003fb0000001e0074006f006f006c005f00700072006f007000650072007400690065007300000001df0000020f00000001000000000000000000000000fc0100000001fb0000000c00740069006d00650072007300000000000000000000000000000000020000010000000000000000000000000000000000000000000200000001000000005f0000012dfc0200000003fb0000001e0074006f006f006c005f00700072006f007000650072007400690065007300000001df0000020f00000001000000000000000000000000fc0100000001fb0000000c00740069006d00650072007300000000000000000000000000000000020000010000000000000000000000000000000000000000000200000001000000005f0000012dfc0200000003fb极0000001e0074006f006f006c005f00700072006f007000650072007400极690065007300000001df0000020f00000001000000000000000000000000fc0100000001fb0000000c00740069006d0065007200730000000000000000000000000000000002000001000000000000000000000000000000000000000000
Selection:
collapsed: false
Time:
collapsed: false
Tool Properties:
collapsed: false
Views:
collapsed: false
Width: 1024
X: 0
Y: 0

View File

@@ -0,0 +1,83 @@
#ifndef TR360_ROS_TR360_TYPES_H
#define TR360_ROS_TR360_TYPES_H
#include <stdint.h>
// 雷达帧头结构
#pragma pack(push, 1)
typedef struct
{
uint32_t a:9; // 方位角
uint32_t r:12; // 距离
int32_t e:11; // 俯仰角
uint8_t v; // rcs
} points_5b;
// 帧头对象新结构
typedef struct
{
unsigned char Outtype; //0 raev 1 xyz
unsigned char Version; //默认4
unsigned char RadarType; //
unsigned char NFrame;
unsigned int Rev[3];
unsigned int FrameCounter; // 帧计数
float Res[4]; // 分辨率
float Offset[4]; // 偏移量
float Time; //雷达输出时间和开始0度采集时间差值
int ObjNum; // 目标个数
unsigned int CheckSum; // 和校验
} FrameHeadObjNewS;
// 雷达头信息结构
typedef struct
{
char header[12]; //帧头 避障"TR360-3-60",地形"TR360-4-60"
uint32_t FrameCounter; // 帧计数
uint32_t nRadar; // 传感器个数避障默认2地形默认1
uint32_t ObjNum; // 总目标个数
uint8_t Rev[36]; //保留
unsigned int CheckSum; //总和校验
} RadarHeaderInfoS;
// 参数限制结构
typedef struct
{
int RLo100; // 距离下限cm
int RHi100; // 距离上限cm
int azLo100; // 方位角下限
int azHi100; // 方位角上限
int elLo100; // 俯仰角下限
int elHi100; // 俯仰角上限
int vLo; // 幅值下限
int vHi; // 幅值上限
int xLo100;
int xHi100;
int yLo100;
int yHi100;
int zLo100;
int zHi100;
int azComp100; // 方位角补偿
int elComp100; // 俯仰角补偿
int Roll100; // 安装滚转角补偿
int Pitch100; // 安装俯仰角补偿
int Yaw100; // 安装方位角补偿
int type;
int xGLo100; // x显示下限
int xGHi100; // x显示上限
int yGLo100; // y显示下限
int yGHi100; // y显示上限
int zGLo100; // z显示下限
int zGHi100; // z显示上限
int vGLo; // 幅度显示下限
int vGHi; // 幅度显示上限
int nStep; // vGLo与vGHi之间显示nStep个色阶
int MaxPoint; //最多显示多少个点
int EleQ;
int ARQ;
int NegQ;
} ParaLim;
#pragma pack(pop)
#endif // TR360_ROS_TR360_TYPES_H

View File

@@ -0,0 +1,53 @@
<launch>
<!-- TR360-3-60 雷达节点 -->
<node name="tr360_3_60_node" pkg="tr360_ros" type="tr360_node" output="screen">
<!-- 雷达类型配置 -->
<param name="radar_type" type="string" value="TR360-3-60" />
<!-- 雷达设备IP和端口 -->
<param name="radar_ip" type="string" value="192.168.1.80" />
<param name="radar_port" type="int" value="10123" />
<!-- 本地接收IP和端口 -->
<param name="local_ip" type="string" value="192.168.1.193" />
<param name="local_port" type="int" value="10195" />
<!-- 雷达数量配置 -->
<param name="max_radars" type="int" value="2" />
<!-- 命令服务器配置 -->
<param name="enable_command_server" type="bool" value="true" />
<param name="max_history_size" type="int" value="100" />
</node>
<!-- TR360-4-60 雷达节点 -->
<node name="tr360_4_60_node" pkg="tr360_ros" type="tr360_node" output="screen">
<!-- 雷达类型配置 -->
<param name="radar_type" type="string" value="TR360-4-60" />
<!-- 雷达设备IP和端口 -->
<param name="radar_ip" type="string" value="192.168.1.81" />
<param name="radar_port" type="int" value="20223" />
<!-- 本地接收IP和端口 -->
<param name="local_ip" type="string" value="192.168.1.193" />
<param name="local_port" type="int" value="20295" />
<!-- 雷达数量配置 -->
<param name="max_radars" type="int" value="1" />
<!-- 命令服务器配置 -->
<param name="enable_command_server" type="bool" value="true" />
<param name="max_history_size" type="int" value="100" />
</node>
<!-- 点云过滤参数(全局参数) -->
<param name="min_range" type="double" value="0.0" />
<param name="max_range" type="double" value="100.0" />
<param name="min_intensity" type="double" value="0.0" />
<param name="max_intensity" type="double" value="100.0" />
<!-- 启动RViz进行点云可视化 -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find tr360_ros)/config/tr360.rviz" />
</launch>

25
src/tr360_ros/package.xml Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<package format="2">
<name>tr360_ros</name>
<version>0.1.0</version>
<description>ROS package for TR360 radar point cloud processing</description>
<maintainer email="yemai@example.com">yemai</maintainer>
<license>BSD</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>pcl_ros</build_depend>
<build_depend>pcl_conversions</build_depend>
<build_depend>dynamic_reconfigure</build_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<exec_depend>pcl_ros</exec_depend>
<exec_depend>pcl_conversions</exec_depend>
<exec_depend>dynamic_reconfigure</exec_depend>
<export>
</export>
</package>

View File

@@ -0,0 +1,66 @@
#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl_conversions/pcl_conversions.h>
#include <cmath>
class TestPublisher
{
private:
ros::NodeHandle nh_;
ros::Publisher cloud_pub_;
ros::Timer timer_;
public:
TestPublisher()
{
cloud_pub_ = nh_.advertise<sensor_msgs::PointCloud2>("tr360_points", 10);
timer_ = nh_.createTimer(ros::Duration(0.1), &TestPublisher::publishTestData, this);
ROS_INFO("Test publisher initialized");
}
void publishTestData(const ros::TimerEvent& event)
{
// 创建测试点云数据
pcl::PointCloud<pcl::PointXYZI> cloud;
cloud.header.frame_id = "tr360_radar";
cloud.is_dense = false;
// 生成模拟雷达数据(圆锥形点云)
int num_points = 1000;
for (int i = 0; i < num_points; ++i) {
float angle = 2.0 * M_PI * i / num_points;
float distance = 5.0 + 2.0 * sin(angle * 5.0);
float height = 2.0 * cos(angle * 3.0);
pcl::PointXYZI point;
point.x = distance * cos(angle);
point.y = distance * sin(angle);
point.z = height;
point.intensity = 50.0 + 30.0 * sin(angle * 2.0);
cloud.points.push_back(point);
}
// 发布点云
sensor_msgs::PointCloud2 cloud_msg;
pcl::toROSMsg(cloud, cloud_msg);
cloud_msg.header.stamp = ros::Time::now();
cloud_msg.header.frame_id = "tr360_radar";
cloud_pub_.publish(cloud_msg);
ROS_INFO("Published %d test points", num_points);
}
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "test_publisher");
TestPublisher publisher;
ros::spin();
return 0;
}

View File

@@ -0,0 +1,459 @@
#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl_conversions/pcl_conversions.h>
#include <dynamic_reconfigure/server.h>
#include "tr360_ros/tr360_types.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <thread>
#include <vector>
class TR360RadarNode
{
private:
ros::NodeHandle nh_;
std::vector<ros::Publisher> cloud_pubs_;
int udp_socket_;
struct sockaddr_in server_addr_, client_addr_;
// 配置参数
std::string radar_type_;
std::string radar_ip_;
int radar_port_;
std::string local_ip_;
int local_port_;
int max_radars_;
// 命令历史记录
std::vector<std::string> command_history_;
int max_history_size_;
// TCP命令服务器
int tcp_socket_;
bool enable_command_server_;
// 点云处理状态
uint32_t state_;
uint32_t total_len_;
uint32_t len1_;
int counter_;
uint8_t header_buf_[128];
uint8_t out_data_[327680];
ParaLim para_obj_;
public:
TR360RadarNode() : state_(0), total_len_(0), len1_(0), counter_(0)
{
// 初始化参数
ros::NodeHandle private_nh("~");
private_nh.param<std::string>("radar_type", radar_type_, "TR360-3-60");
private_nh.param<std::string>("radar_ip", radar_ip_, "192.168.1.80");
private_nh.param("radar_port", radar_port_, 10123);
private_nh.param<std::string>("local_ip", local_ip_, "192.168.1.193");
private_nh.param("local_port", local_port_, 10193);
private_nh.param("max_radars", max_radars_, 4); // 最大雷达数量
private_nh.param("enable_command_server", enable_command_server_, true);
private_nh.param("max_history_size", max_history_size_, 100);
// 创建点云发布者(支持多个雷达)
for (int i = 0; i < max_radars_; ++i) {
std::string topic_name;
if (radar_type_ == "TR360-3-60" && max_radars_ == 2) {
// TR360-3-60 双雷达配置
if (i == 0) topic_name = "tr360_3_30_up";
else if (i == 1) topic_name = "tr360_3_60_front";
else topic_name = "tr360_points_" + std::to_string(i+1);
} else if (radar_type_ == "TR360-3-60" && max_radars_ == 1) {
// TR360-3-60 单雷达配置
topic_name = "tr360_4_60_terrain";
} else {
// 默认配置
topic_name = "tr360_points_" + std::to_string(i+1);
}
cloud_pubs_.push_back(nh_.advertise<sensor_msgs::PointCloud2>(topic_name, 10));
}
// 初始化UDP socket
initUDP();
// 初始化TCP命令服务器
if (enable_command_server_) {
initCommandServer();
}
// 初始化参数对象
initParameters();
}
void initUDP()
{
udp_socket_ = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_ < 0) {
ROS_ERROR("Failed to create socket");
return;
}
// 设置本地地址
memset(&server_addr_, 0, sizeof(server_addr_));
server_addr_.sin_family = AF_INET;
server_addr_.sin_addr.s_addr = inet_addr(local_ip_.c_str());
server_addr_.sin_port = htons(local_port_);
// 绑定socket
if (bind(udp_socket_, (struct sockaddr*)&server_addr_, sizeof(server_addr_)) < 0) {
ROS_ERROR("Bind failed");
close(udp_socket_);
return;
}
ROS_INFO("UDP socket initialized on %s:%d", local_ip_.c_str(), local_port_);
// 根据雷达类型设置默认端口
if (radar_type_ == "TR360-3-60") {
local_port_ = 10195;
} else if (radar_type_ == "TR360-4-60") {
local_port_ = 20295;
}
}
void initParameters()
{
// 初始化参数默认值
para_obj_.RLo100 = 0;
para_obj_.RHi100 = 4000;
para_obj_.azLo100 = 0;
para_obj_.azHi100 = 36000;
para_obj_.elLo100 = -12000;
para_obj_.elHi100 = 12000;
para_obj_.vLo = 0;
para_obj_.vHi = 100;
para_obj_.xLo100 = -4000;
para_obj_.xHi100 = 4000;
para_obj_.yLo100 = -4000;
para_obj_.yHi100 = 4000;
para_obj_.zLo100 = -4000;
para_obj_.zHi100 = 4000;
para_obj_.azComp100 = 0;
para_obj_.elComp100 = 0;
para_obj_.Roll100 = 0;
para_obj_.Pitch100 = 0;
para_obj_.Yaw100 = 0;
para_obj_.xGLo100 = -4000;
para_obj_.xGHi100 = 4000;
para_obj_.yGLo100 = -4000;
para_obj_.yGHi100 = 4000;
para_obj_.zGLo100 = -4000;
para_obj_.zGHi100 = 4000;
para_obj_.vGLo = 20;
para_obj_.vGHi = 60;
para_obj_.nStep = 33;
para_obj_.MaxPoint = 20000;
para_obj_.EleQ = 0;
para_obj_.ARQ = 0;
para_obj_.NegQ = 0;
}
int parseFrame(unsigned char *data, unsigned int len)
{
RadarHeaderInfoS *ptr = (RadarHeaderInfoS *)data;
FrameHeadObjNewS *frame_ptr;
const char *header = radar_type_.c_str();
if (strncmp(header, (char*)ptr->header, strlen(header)) == 0) {
frame_ptr = (FrameHeadObjNewS *)(data + 64);
if (state_ != 3) {
ROS_DEBUG("Counter:%d, len1:%d, total_len:%d, ObjNum:%d, nRadar:%d, RadarObj:%d",
counter_, len1_, total_len_, ptr->ObjNum, ptr->nRadar, frame_ptr->ObjNum);
}
if (len >= 128) {
memcpy(header_buf_, data, 128);
} else {
memset(header_buf_, 0, 128);
}
total_len_ = ptr->ObjNum * 5 + ptr->nRadar * 64 + 64;
len1_ = len;
state_ = 1;
} else if (state_ == 1) {
len1_ += len;
state_ = 2;
} else if (state_ == 2) {
len1_ += len;
state_ = 2;
}
if (state_ == 1) {
counter_++;
if ((total_len_ < 0) || (total_len_ > 60000)) {
ROS_WARN("Invalid total length: %d", total_len_);
total_len_ = 0;
state_ = 0;
}
}
if (state_) {
if (len1_ > total_len_) {
ptr = (RadarHeaderInfoS *)header_buf_;
frame_ptr = (FrameHeadObjNewS *)(header_buf_ + 64);
ROS_WARN("Data length exceeded: Counter:%d, len1:%d, total_len:%d",
counter_, len1_, total_len_);
total_len_ = 0;
state_ = 0;
} else if (len1_ == total_len_) {
total_len_ = 0;
state_ = 3;
return state_;
}
}
return state_;
}
void callback_arev5b(float *arve, void *ptr, float *factor4, float *offset4)
{
points_5b *point_ptr = (points_5b *)ptr;
arve[0] = point_ptr->a * factor4[0] + offset4[0];
arve[1] = point_ptr->r * factor4[1] + offset4[1];
arve[2] = point_ptr->e * factor4[2];
arve[3] = point_ptr->v * factor4[3] + offset4[3];
}
void processPointCloud(unsigned char *data, unsigned int len)
{
RadarHeaderInfoS *radar_info_ptr = (RadarHeaderInfoS *)data;
if (radar_info_ptr->nRadar == 0) {
return;
}
// 处理所有雷达数据
for (int radar_idx = 0; radar_idx < radar_info_ptr->nRadar; ++radar_idx) {
if (radar_idx >= cloud_pubs_.size()) {
ROS_WARN("Radar index %d exceeds configured publisher count", radar_idx);
continue;
}
FrameHeadObjNewS frame_obj = *(FrameHeadObjNewS *)(data + 64 * radar_idx + 64);
// 创建PCL点云
pcl::PointCloud<pcl::PointXYZI> cloud;
cloud.header.frame_id = "tr360_radar";
cloud.is_dense = false;
// 解析点云数据
points_5b *points = (points_5b *)(data + len - 5 * frame_obj.ObjNum);
for (int i = 0; i < frame_obj.ObjNum; ++i) {
points_5b point = points[i];
// 转换为直角坐标系
float a_rad = point.a * frame_obj.Res[0] + frame_obj.Offset[0];
float r = point.r * frame_obj.Res[1] + frame_obj.Offset[1];
float e_rad = point.e * frame_obj.Res[2];
float intensity = point.v * frame_obj.Res[3] + frame_obj.Offset[3];
// 坐标转换: {x,y,z,v} = {r*cos(e)*cos(a), r*cos(e)*sin(a), r*sin(e), v}
pcl::PointXYZI pcl_point;
pcl_point.x = r * cos(e_rad) * cos(a_rad);
pcl_point.y = r * cos(e_rad) * sin(a_rad);
pcl_point.z = r * sin(e_rad);
pcl_point.intensity = intensity;
cloud.points.push_back(pcl_point);
}
// 发布点云
sensor_msgs::PointCloud2 cloud_msg;
pcl::toROSMsg(cloud, cloud_msg);
cloud_msg.header.stamp = ros::Time::now();
cloud_msg.header.frame_id = "tr360_radar_" + std::to_string(radar_idx + 1);
cloud_pubs_[radar_idx].publish(cloud_msg);
ROS_INFO("Published %lu points from radar %d", cloud.points.size(), radar_idx + 1);
}
}
void initCommandServer()
{
tcp_socket_ = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_socket_ < 0) {
ROS_ERROR("Failed to create TCP socket");
return;
}
// 设置TCP服务器地址
struct sockaddr_in tcp_addr;
memset(&tcp_addr, 0, sizeof(tcp_addr));
tcp_addr.sin_family = AF_INET;
tcp_addr.sin_addr.s_addr = inet_addr(local_ip_.c_str());
tcp_addr.sin_port = htons(local_port_ + 1000); // 命令端口为数据端口+1000
// 绑定并监听
if (bind(tcp_socket_, (struct sockaddr*)&tcp_addr, sizeof(tcp_addr)) < 0) {
ROS_ERROR("TCP bind failed");
close(tcp_socket_);
return;
}
if (listen(tcp_socket_, 5) < 0) {
ROS_ERROR("TCP listen failed");
close(tcp_socket_);
return;
}
ROS_INFO("TCP command server started on %s:%d", local_ip_.c_str(), local_port_ + 1000);
}
void handleCommand(int client_sock)
{
char buffer[1024];
int recv_len = recv(client_sock, buffer, sizeof(buffer) - 1, 0);
if (recv_len > 0) {
buffer[recv_len] = '\0';
std::string command(buffer);
// 去除回车换行
size_t pos;
while ((pos = command.find("\r\n")) != std::string::npos) {
command.erase(pos, 2);
}
while ((pos = command.find('\n')) != std::string::npos) {
command.erase(pos, 1);
}
while ((pos = command.find('\r')) != std::string::npos) {
command.erase(pos, 1);
}
// 添加到历史记录
addToHistory(command);
// 处理命令
std::string response = processRadarCommand(command);
// 发送响应(添加回车换行)
response += "\r\n";
send(client_sock, response.c_str(), response.length(), 0);
ROS_INFO("Command: %s -> Response: %s", command.c_str(), response.c_str());
}
close(client_sock);
}
void addToHistory(const std::string& command)
{
command_history_.push_back(command);
if (command_history_.size() > max_history_size_) {
command_history_.erase(command_history_.begin());
}
}
std::string processRadarCommand(const std::string& command)
{
if (command == "history") {
std::string history;
for (size_t i = 0; i < command_history_.size(); ++i) {
history += std::to_string(i + 1) + ": " + command_history_[i] + "\n";
}
return history.empty() ? "No command history" : history;
}
else if (command == "status") {
return "Radar type: " + radar_type_ + ", Port: " + std::to_string(local_port_);
}
else if (command.find("set_port") == 0) {
// 设置端口命令格式: set_port 10195
size_t space_pos = command.find(' ');
if (space_pos != std::string::npos) {
try {
int new_port = std::stoi(command.substr(space_pos + 1));
local_port_ = new_port;
return "Port set to: " + std::to_string(new_port);
} catch (...) {
return "Invalid port number";
}
}
return "Usage: set_port <port_number>";
}
// 默认响应 - 模拟雷达命令响应
return "ACK: " + command;
}
void run()
{
unsigned char buffer[1500];
socklen_t client_len = sizeof(client_addr_);
fd_set read_fds;
int max_fd;
while (ros::ok()) {
FD_ZERO(&read_fds);
FD_SET(udp_socket_, &read_fds);
max_fd = udp_socket_;
if (enable_command_server_) {
FD_SET(tcp_socket_, &read_fds);
if (tcp_socket_ > max_fd) max_fd = tcp_socket_;
}
struct timeval timeout = {0, 100000}; // 100ms timeout
int activity = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
if (activity > 0) {
// 处理UDP数据
if (FD_ISSET(udp_socket_, &read_fds)) {
int recv_len = recvfrom(udp_socket_, buffer, sizeof(buffer), 0,
(struct sockaddr*)&client_addr_, &client_len);
if (recv_len > 0) {
int result = parseFrame(buffer, recv_len);
if (result == 3) {
processPointCloud(buffer, recv_len);
}
}
}
// 处理TCP命令连接
if (enable_command_server_ && FD_ISSET(tcp_socket_, &read_fds)) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock = accept(tcp_socket_, (struct sockaddr*)&client_addr, &client_len);
if (client_sock >= 0) {
// 在新线程中处理命令
std::thread(&TR360RadarNode::handleCommand, this, client_sock).detach();
}
}
}
ros::spinOnce();
}
close(udp_socket_);
if (enable_command_server_) {
close(tcp_socket_);
}
}
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "tr360_radar_node");
TR360RadarNode node;
node.run();
return 0;
}