增加雷达
This commit is contained in:
142
README.md
142
README.md
@@ -63,18 +63,58 @@ curl http://{IP}:8765/state
|
||||
|
||||
```json
|
||||
{
|
||||
"x": -2.0,
|
||||
"y": -2.3,
|
||||
"z": 0.08,
|
||||
"yaw_deg": 0.0,
|
||||
"speed_target": 1.0,
|
||||
"speed_actual": 0.98,
|
||||
"steer_target": 0.0,
|
||||
"speed_amp": 1.0,
|
||||
"steer_amp": 0.35
|
||||
"x": -2.0, "y": -2.3, "z": 0.08, "yaw_deg": 0.0,
|
||||
"vx": 0.98, "vy": 0.0, "wx": 0.0, "wy": 0.0, "wz": 0.01,
|
||||
"speed_target": 1.0, "speed_actual": 0.98, "steer_target": 0.0,
|
||||
"speed_amp": 1.0, "steer_amp": 0.35
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/scan`
|
||||
|
||||
模拟 LSLIDAR N10 2D 激光雷达(360°, 450 点)。
|
||||
|
||||
```bash
|
||||
curl http://{IP}:8765/scan
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"angle_min": -3.14, "angle_max": 3.14, "angle_increment": 0.014,
|
||||
"range_min": 0.15, "range_max": 3.0,
|
||||
"ranges": [0.52, 0.51, 0.53, ...]
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/odom`
|
||||
|
||||
里程计数据(匹配 `nav_msgs/Odometry`)。
|
||||
|
||||
```bash
|
||||
curl http://{IP}:8765/odom
|
||||
```
|
||||
|
||||
返回示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"x": -2.0, "y": -2.3, "yaw": 3.14,
|
||||
"vx": 0.98, "vy": 0.0, "wz": 0.01,
|
||||
"orientation": {"w": 0.0, "x": 0.0, "y": 0.0, "z": 1.0}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/power`
|
||||
|
||||
电池电压(模拟 12V)。
|
||||
|
||||
```bash
|
||||
curl http://{IP}:8765/power
|
||||
# → {"voltage": 12.0}
|
||||
```
|
||||
|
||||
### GET `/imu`
|
||||
|
||||
获取 IMU 数据(含 MPU6050 噪声),JSON。
|
||||
@@ -107,11 +147,28 @@ curl -X POST http://{IP}:8765/cmd \
|
||||
-d '{"speed": 1.0, "steer": 0.3}'
|
||||
```
|
||||
|
||||
**Ackermann 模式**:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `speed` | float | 目标速度 m/s,正=前进,负=后退 |
|
||||
| `steer` | float | 目标转向角 rad,正=左转,负=右转 |
|
||||
|
||||
**差速模式**(兼容 `cmd_vel/Twist`):
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `vx` | float | 线速度 m/s |
|
||||
| `vy` | float | 横向速度 m/s(忽略) |
|
||||
| `wz` | float | 角速度 rad/s,自动转为转向角 |
|
||||
|
||||
```bash
|
||||
# 差速模式
|
||||
curl -X POST http://{IP}:8765/cmd \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"vx": 1.0, "wz": 0.5}'
|
||||
```
|
||||
|
||||
**Python 客户端示例**:
|
||||
|
||||
```python
|
||||
@@ -135,40 +192,69 @@ time.sleep(2)
|
||||
requests.post(f"http://{IP}:8765/cmd", json={"speed": 0.0, "steer": 0.0})
|
||||
```
|
||||
|
||||
**ROS2 桥接示例**:
|
||||
**ROS2 桥接示例**(仿真机 HTTP → ROS2 局域网):
|
||||
|
||||
```python
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from sensor_msgs.msg import Image as RosImage
|
||||
from geometry_msgs.msg import Twist
|
||||
from cv_bridge import CvBridge
|
||||
import requests
|
||||
import numpy as np
|
||||
import cv2
|
||||
from sensor_msgs.msg import Image, Imu, LaserScan
|
||||
from nav_msgs.msg import Odometry
|
||||
from geometry_msgs.msg import Twist, Quaternion
|
||||
from std_msgs.msg import Float32
|
||||
import requests, math, numpy as np, cv2
|
||||
|
||||
IP = "192.168.1.100"
|
||||
|
||||
class OrigincarBridge(Node):
|
||||
def __init__(self):
|
||||
super().__init__("origincar_bridge")
|
||||
self.bridge = CvBridge()
|
||||
self.pub = self.create_publisher(RosImage, "/image", 10)
|
||||
self.pub_scan = self.create_publisher(LaserScan, "/scan", 10)
|
||||
self.pub_odom = self.create_publisher(Odometry, "/odom", 10)
|
||||
self.pub_imu = self.create_publisher(Imu, "/imu/data_raw", 10)
|
||||
self.pub_pwr = self.create_publisher(Float32, "/PowerVoltage", 10)
|
||||
self.sub = self.create_subscription(Twist, "/cmd_vel", self.cmd_cb, 10)
|
||||
self.timer = self.create_timer(0.1, self.fetch_and_publish)
|
||||
self.timer = self.create_timer(0.033, self.sync) # ~30Hz
|
||||
|
||||
def fetch_and_publish(self):
|
||||
r = requests.get(f"http://{IP}:8765/image", timeout=1)
|
||||
if r.status_code == 200:
|
||||
arr = np.frombuffer(r.content, np.uint8)
|
||||
cv_img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
|
||||
msg = self.bridge.cv2_to_imgmsg(cv_img, "bgr8")
|
||||
self.pub.publish(msg)
|
||||
def sync(self):
|
||||
# LaserScan
|
||||
data = requests.get(f"http://{IP}:8765/scan", timeout=1).json()
|
||||
msg = LaserScan()
|
||||
msg.header.frame_id = "laser"; msg.header.stamp = self.get_clock().now().to_msg()
|
||||
msg.angle_min = data["angle_min"]; msg.angle_max = data["angle_max"]
|
||||
msg.angle_increment = data["angle_increment"]
|
||||
msg.range_min = data["range_min"]; msg.range_max = data["range_max"]
|
||||
msg.ranges = data["ranges"]
|
||||
self.pub_scan.publish(msg)
|
||||
|
||||
# Odometry
|
||||
odom = requests.get(f"http://{IP}:8765/odom", timeout=1).json()
|
||||
o = Odometry()
|
||||
o.header.frame_id = "odom"; o.child_frame_id = "base_link"
|
||||
o.pose.pose.position.x = odom["x"]; o.pose.pose.position.y = odom["y"]
|
||||
o.pose.pose.orientation = Quaternion(**odom["orientation"])
|
||||
o.twist.twist.linear.x = odom["vx"]; o.twist.twist.angular.z = odom["wz"]
|
||||
self.pub_odom.publish(o)
|
||||
|
||||
# IMU
|
||||
imu_d = requests.get(f"http://{IP}:8765/imu", timeout=1).json()
|
||||
imu = Imu()
|
||||
imu.header.frame_id = "gyro_link"
|
||||
imu.orientation = Quaternion(**imu_d["orientation"])
|
||||
imu.angular_velocity.x = imu_d["angular_velocity"]["x"]
|
||||
imu.angular_velocity.y = imu_d["angular_velocity"]["y"]
|
||||
imu.angular_velocity.z = imu_d["angular_velocity"]["z"]
|
||||
imu.linear_acceleration.x = imu_d["linear_acceleration"]["x"]
|
||||
imu.linear_acceleration.y = imu_d["linear_acceleration"]["y"]
|
||||
imu.linear_acceleration.z = imu_d["linear_acceleration"]["z"]
|
||||
self.pub_imu.publish(imu)
|
||||
|
||||
# Power
|
||||
p = Float32(data=requests.get(f"http://{IP}:8765/power", timeout=1).json()["voltage"])
|
||||
self.pub_pwr.publish(p)
|
||||
|
||||
def cmd_cb(self, msg: Twist):
|
||||
requests.post(f"http://{IP}:8765/cmd", json={
|
||||
"speed": msg.linear.x,
|
||||
"steer": msg.angular.z
|
||||
"vx": msg.linear.x, "wz": msg.angular.z
|
||||
})
|
||||
|
||||
rclpy.init()
|
||||
|
||||
Reference in New Issue
Block a user