feat: separate laser API endpoint (POST /api/laser)

This commit is contained in:
dev01
2026-06-01 20:46:46 +08:00
parent a281fd115e
commit bb8724035f
3 changed files with 95 additions and 42 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ server {
proxy_send_timeout 86400s;
}
# 模式切换 API (HTTP → ROS2 web_node)
# API (HTTP → ROS2 web_node)
location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
proxy_set_header Host $host;
+27 -11
View File
@@ -109,11 +109,6 @@ curl -X POST http://localhost:8080/api/mode/switch \
curl -X POST http://localhost:8080/api/mode/switch \
-H "Content-Type: application/json" \
-d '{"mode": "debug"}'
# 激光开关
curl -X POST http://localhost:8080/api/mode/switch \
-H "Content-Type: application/json" \
-d '{"mode": "laser_on"}'
```
也可直接通过 ROS2 话题发送:
@@ -122,7 +117,28 @@ curl -X POST http://localhost:8080/api/mode/switch \
ros2 topic pub /gimbal/mode std_msgs/String "data: 'auto'"
```
## 6. 手动控制云台
## 6. 激光控制
```bash
# 开激光
curl -X POST http://localhost:8080/api/laser \
-H "Content-Type: application/json" \
-d '{"action": "on"}'
# 关激光
curl -X POST http://localhost:8080/api/laser \
-H "Content-Type: application/json" \
-d '{"action": "off"}'
```
也可直接通过 ROS2 话题发送:
```bash
ros2 topic pub /gimbal/mode std_msgs/String "data: 'laser_on'"
ros2 topic pub /gimbal/mode std_msgs/String "data: 'laser_off'"
```
## 7. 手动控制云台
```bash
# angular.y = pitch 角速度, angular.z = yaw 角速度
@@ -130,7 +146,7 @@ ros2 topic pub /cmd_gimbal geometry_msgs/Twist \
"{angular: {y: 0.0, z: 0.0}}"
```
## 7. PID 调参(调试模式)
## 8. PID 调参(调试模式)
先切换到 debug 模式,然后通过 `/pid_config` 话题发送参数:
@@ -144,13 +160,13 @@ ros2 topic pub /pid_config std_msgs/String \
参数立即生效,调试完毕后切回 auto 或 manual 模式。
## 8. 查看视觉结果
## 9. 查看视觉结果
浏览器打开 `http://<设备IP>:8080`,可看到:
- 原始图像 + 轮廓标注(result)
- Otsu 二值化 + 闭运算结果(binary)
## 9. 查看话题
## 10. 查看话题
```bash
# 列出所有活跃话题
@@ -164,7 +180,7 @@ ros2 topic hz /vision/target
ros2 topic echo /vision/target
```
## 10. 测试
## 11. 测试
```bash
cd workspace
@@ -179,7 +195,7 @@ colcon test --packages-select gimbal_control_pkg
colcon test-result --all
```
## 11. 故障排查
## 12. 故障排查
| 现象 | 检查项 |
|------|--------|
@@ -89,6 +89,14 @@ class _ImageHandler(BaseHTTPRequestHandler):
def do_POST(self):
path = self.path.split('?')[0]
if path == '/api/mode/switch':
self._handle_mode_switch()
elif path == '/api/laser':
self._handle_laser()
else:
self.send_response(404)
self.end_headers()
def _handle_mode_switch(self):
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length) if content_length > 0 else b'{}'
try:
@@ -100,7 +108,7 @@ class _ImageHandler(BaseHTTPRequestHandler):
self.wfile.write(b'{"error":"invalid json"}')
return
valid_modes = ('auto', 'manual', 'debug', 'laser_on', 'laser_off')
valid_modes = ('auto', 'manual', 'debug')
if mode not in valid_modes:
self.send_response(400)
self.end_headers()
@@ -119,9 +127,38 @@ class _ImageHandler(BaseHTTPRequestHandler):
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(f'{{"status":"ok","mode":"{mode}"}}'.encode())
else:
self.send_response(404)
def _handle_laser(self):
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length) if content_length > 0 else b'{}'
try:
data = json.loads(body)
action = data.get('action', '')
except json.JSONDecodeError:
self.send_response(400)
self.end_headers()
self.wfile.write(b'{"error":"invalid json"}')
return
if action == 'on':
mode = 'laser_on'
elif action == 'off':
mode = 'laser_off'
else:
self.send_response(400)
self.end_headers()
self.wfile.write(b'{"error":"action must be on or off"}')
return
if _mode_publisher is not None:
msg = String()
msg.data = mode
_mode_publisher.publish(msg)
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(f'{{"status":"ok","laser":"{action}"}}'.encode())
def _serve_image(self, key):
global _latest_jpeg