Merge branch 'main-v2' of ssh://120.27.199.238:222/Havoc420mac/mi-task into main-v2
This commit is contained in:
commit
df0f010a52
33
main.py
33
main.py
@ -60,42 +60,27 @@ def main():
|
||||
msg = robot_control_cmd_lcmt()
|
||||
print('2')
|
||||
try:
|
||||
# TEST
|
||||
# print('yuyin')
|
||||
# cv_image = Ctrl.image_processor.get_current_image('ai')
|
||||
# print('111')
|
||||
# cv2.imwrite(f"saved_images/firstai.jpg", cv_image)
|
||||
# print('try out')
|
||||
# speak('nihao')
|
||||
|
||||
# TEST
|
||||
# go_straight_with_enhanced_calibration(Ctrl, msg, distance = 5, speed=0.5, observe=False, mode=11, gait_id=3, step_height=[0.21, 0.21])
|
||||
# pass_up_down(Ctrl, msg)
|
||||
# pass_bar(Ctrl, msg)
|
||||
# run_task_2(Ctrl, msg)
|
||||
|
||||
# INFO Real Task
|
||||
# TAG task - 0
|
||||
info("Recovery stand", "info")
|
||||
Ctrl.base_msg.stand_up()
|
||||
Ctrl.base_msg.stop() # BUG 垃圾指令 for eat # INFO 但是正式比赛或许也有用
|
||||
|
||||
# image = Ctrl.image_processor.get_current_image('ai')
|
||||
# timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
||||
# filename = f"saved_images/rgb_{timestamp}.jpg"
|
||||
# cv2.imwrite(filename, image)
|
||||
|
||||
if TASK == TaskType.PASS_BAR:
|
||||
from task_4.pass_bar import pass_bar
|
||||
# TEST #1: pass-bar
|
||||
pass_bar(Ctrl, msg)
|
||||
elif TASK == TaskType.YELLOW_LIGHT: # TODO image
|
||||
from task_3.task_3 import go_until_yellow_area
|
||||
<<<<<<< HEAD
|
||||
# turn_degree_v2(Ctrl, msg, degree=-90, absolute=True)
|
||||
go_until_yellow_area(Ctrl, msg, yellow_ratio_threshold=0.012, speed=0.3, max_time=30, observe=True)
|
||||
=======
|
||||
go_until_yellow_area(Ctrl, msg, yellow_ratio_threshold=0.05)
|
||||
>>>>>>> 00b77fe2476f74be45267ef52a45138e41114e7b
|
||||
elif TASK == TaskType.RED_BAR:
|
||||
from task_4.task_4 import go_straight_until_red_bar
|
||||
go_straight_until_red_bar(Ctrl, msg)
|
||||
go_straight_until_red_bar(Ctrl, msg, red_ratio_threshold=0.18, step_distance=0.3)
|
||||
elif TASK == TaskType.UP_AND_DOWN:
|
||||
from task_3.task_3 import go_straight_with_enhanced_calibration
|
||||
go_straight_with_enhanced_calibration(Ctrl, msg, distance = 5, speed=0.5, observe=False, mode=11, gait_id=3, step_height=[0.21, 0.21])
|
||||
@ -108,16 +93,18 @@ def main():
|
||||
elif TASK == TaskType.CENTER_ON_DUAL_TRACKS:
|
||||
from base_move.center_on_dual_tracks import center_on_dual_tracks
|
||||
center_on_dual_tracks(Ctrl, msg, max_deviation=10.0, observe=False, detect_height=0.3)
|
||||
<<<<<<< HEAD
|
||||
else:
|
||||
pass
|
||||
|
||||
=======
|
||||
>>>>>>> 00b77fe2476f74be45267ef52a45138e41114e7b
|
||||
if TASK != TaskType.TASK:
|
||||
# 如果不是 task 类型,直接返回
|
||||
pass
|
||||
|
||||
# TAG task - 1
|
||||
# run_task_1(Ctrl, msg, time_sleep=TIME_SLEEP)
|
||||
|
||||
|
||||
# TAG task - 2
|
||||
# arrow_direction='left'
|
||||
@ -126,8 +113,12 @@ def main():
|
||||
# arrow_direction='left'
|
||||
# print('🏹 arrow_direction: ', arrow_direction)
|
||||
|
||||
<<<<<<< HEAD
|
||||
|
||||
# arrow_direction = 'right'
|
||||
=======
|
||||
|
||||
>>>>>>> 00b77fe2476f74be45267ef52a45138e41114e7b
|
||||
# if(arrow_direction=='left'): speak("左侧路线")
|
||||
# else: speak("右侧路线")
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ from utils.speech_demo import speak
|
||||
logger = get_logger("任务3")
|
||||
|
||||
observe = False
|
||||
YELLOW_RATIO_THRESHOLD = 0.03 # TODO 黄色区域比例阈值
|
||||
YELLOW_RATIO_THRESHOLD = 0.04 # TODO 黄色区域比例阈值
|
||||
|
||||
def run_task_3(ctrl, msg, time_sleep=5000):
|
||||
section('任务3:上下坡', "启动")
|
||||
|
||||
@ -87,7 +87,7 @@ def run_task_4_back(ctrl, msg):
|
||||
|
||||
def go_straight_until_red_bar(ctrl, msg,
|
||||
red_ratio_threshold=0.2,
|
||||
step_distance=0.5,
|
||||
step_distance=0.3,
|
||||
max_distance=5,
|
||||
speed=0.3
|
||||
):
|
||||
|
||||
49
test/text-image/README.md
Normal file
49
test/text-image/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
# 数字识别程序
|
||||
|
||||
这是一个使用OpenCV识别图片中数字1和2的Python程序。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- 使用OpenCV进行图像预处理和特征提取
|
||||
- 基于轮廓检测和特征分析的数字分类
|
||||
- 能够区分数字1和2的形态特征
|
||||
- 自动保存识别结果图片
|
||||
|
||||
## 安装依赖
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 将需要识别的图片放在`imgs/`文件夹中
|
||||
2. 运行程序:
|
||||
```bash
|
||||
python text_image.py
|
||||
```
|
||||
|
||||
## 程序流程
|
||||
|
||||
1. **图像预处理**:转换为灰度图、高斯模糊去噪、自适应阈值二值化、形态学操作
|
||||
2. **轮廓检测**:查找图像中的轮廓,筛选可能的数字区域
|
||||
3. **特征提取**:提取ROI的像素密度、水平和垂直投影等特征
|
||||
4. **数字分类**:基于特征分析判断是数字1还是2
|
||||
5. **结果输出**:在原图上标注识别结果并保存
|
||||
|
||||
## 识别原理
|
||||
|
||||
- **数字1**:通常较细,垂直投影集中在中间,方差较小
|
||||
- **数字2**:通常较宽,有弯曲部分,垂直投影方差较大
|
||||
|
||||
## 输出文件
|
||||
|
||||
程序会在`imgs/`文件夹中生成:
|
||||
- `result_1.jpg`:第一张图片的识别结果
|
||||
- `result_2.jpg`:第二张图片的识别结果
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 确保图片清晰,数字对比度足够
|
||||
- 程序会自动过滤太小的轮廓
|
||||
- 识别结果包含位置信息和置信度
|
||||
BIN
test/text-image/imgs/1.jpg
Normal file
BIN
test/text-image/imgs/1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
BIN
test/text-image/imgs/2.jpg
Normal file
BIN
test/text-image/imgs/2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
test/text-image/imgs/result_1.jpg
Normal file
BIN
test/text-image/imgs/result_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
BIN
test/text-image/imgs/result_2.jpg
Normal file
BIN
test/text-image/imgs/result_2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
3
test/text-image/requirements.txt
Normal file
3
test/text-image/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
opencv-python>=4.5.0
|
||||
numpy>=1.19.0
|
||||
pathlib
|
||||
180
test/text-image/text_image.py
Normal file
180
test/text-image/text_image.py
Normal file
@ -0,0 +1,180 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def preprocess_image(image):
|
||||
"""
|
||||
预处理图像:转换为HSV色彩空间,检测白色区域
|
||||
"""
|
||||
# 转换为HSV色彩空间
|
||||
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
||||
|
||||
# 定义白色的HSV范围 - 放宽范围
|
||||
# 白色在HSV中:饱和度低,明度高
|
||||
# 原来的范围:lower_white = np.array([0, 0, 200]), upper_white = np.array([180, 30, 255])
|
||||
# 放宽后的范围:
|
||||
lower_white = np.array([0, 0, 150]) # 降低明度下限,从200降到150
|
||||
upper_white = np.array([180, 60, 255]) # 提高饱和度上限,从30提高到60
|
||||
|
||||
# 创建白色掩码
|
||||
white_mask = cv2.inRange(hsv, lower_white, upper_white)
|
||||
|
||||
# 形态学操作:开运算去除小噪点,闭运算填充小孔
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
|
||||
cleaned = cv2.morphologyEx(white_mask, cv2.MORPH_OPEN, kernel)
|
||||
cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel)
|
||||
|
||||
return cleaned
|
||||
|
||||
def find_white_rectangles(binary_image):
|
||||
"""
|
||||
查找白色矩形区域
|
||||
"""
|
||||
contours, _ = cv2.findContours(
|
||||
binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
|
||||
)
|
||||
|
||||
# 筛选合适的矩形轮廓
|
||||
valid_rectangles = []
|
||||
for contour in contours:
|
||||
area = cv2.contourArea(contour)
|
||||
if area > 200: # 过滤太小的区域,从500降低到200
|
||||
# 计算轮廓的边界矩形
|
||||
x, y, w, h = cv2.boundingRect(contour)
|
||||
|
||||
# 计算轮廓的近似多边形
|
||||
epsilon = 0.02 * cv2.arcLength(contour, True)
|
||||
approx = cv2.approxPolyDP(contour, epsilon, True)
|
||||
|
||||
# 检查是否为四边形(矩形)
|
||||
if len(approx) == 4:
|
||||
# 计算宽高比
|
||||
aspect_ratio = w / h
|
||||
# 矩形应该有合理的宽高比(不是太细长)
|
||||
if 0.3 < aspect_ratio < 3.0:
|
||||
valid_rectangles.append((contour, (x, y, w, h), approx))
|
||||
|
||||
return valid_rectangles
|
||||
|
||||
def analyze_white_region(roi, original_roi):
|
||||
"""
|
||||
分析白色区域的特征
|
||||
"""
|
||||
# 计算白色像素比例
|
||||
total_pixels = roi.shape[0] * roi.shape[1]
|
||||
white_pixels = np.sum(roi == 255)
|
||||
white_ratio = white_pixels / total_pixels
|
||||
|
||||
# 计算区域的形状特征
|
||||
height, width = roi.shape
|
||||
aspect_ratio = width / height
|
||||
|
||||
# 计算边缘强度(白色区域应该有清晰的边缘)
|
||||
edges = cv2.Canny(original_roi, 50, 150)
|
||||
edge_density = np.sum(edges > 0) / total_pixels
|
||||
|
||||
return {
|
||||
'white_ratio': white_ratio,
|
||||
'aspect_ratio': aspect_ratio,
|
||||
'edge_density': edge_density,
|
||||
'area': total_pixels
|
||||
}
|
||||
|
||||
def detect_white_rectangles_in_image(image_path):
|
||||
"""
|
||||
检测图片中的白色矩形区域
|
||||
"""
|
||||
# 读取图像
|
||||
image = cv2.imread(image_path)
|
||||
if image is None:
|
||||
print(f"无法读取图像: {image_path}")
|
||||
return []
|
||||
|
||||
# 预处理
|
||||
white_mask = preprocess_image(image)
|
||||
|
||||
# 查找白色矩形
|
||||
valid_rectangles = find_white_rectangles(white_mask)
|
||||
|
||||
results = []
|
||||
|
||||
for contour, (x, y, w, h), approx in valid_rectangles:
|
||||
# 提取ROI
|
||||
roi_mask = white_mask[y:y+h, x:x+w]
|
||||
roi_original = image[y:y+h, x:x+w]
|
||||
|
||||
# 分析特征
|
||||
features = analyze_white_region(roi_mask, roi_original)
|
||||
|
||||
# 判断是否为有效的白色矩形
|
||||
is_valid_white_rectangle = (
|
||||
features['white_ratio'] > 0.4 and # 白色像素比例要求降低,从0.6降到0.4
|
||||
features['edge_density'] > 0.005 # 边缘密度要求降低,从0.01降到0.005
|
||||
)
|
||||
|
||||
if is_valid_white_rectangle:
|
||||
results.append({
|
||||
'type': 'white_rectangle',
|
||||
'position': (x, y, w, h),
|
||||
'features': features,
|
||||
'confidence': 'high' if features['white_ratio'] > 0.8 else 'medium'
|
||||
})
|
||||
|
||||
# 在图像上绘制结果
|
||||
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
|
||||
cv2.putText(image, f"白色矩形", (x, y - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
|
||||
|
||||
# 绘制轮廓点
|
||||
cv2.drawContours(image, [approx], -1, (255, 0, 0), 2)
|
||||
|
||||
return image, results
|
||||
|
||||
def main():
|
||||
"""
|
||||
主函数:测试白色矩形检测
|
||||
"""
|
||||
# 图片路径
|
||||
img_dir = Path("imgs")
|
||||
img1_path = img_dir / "1.jpg"
|
||||
img2_path = img_dir / "2.jpg"
|
||||
|
||||
print("开始检测图片中的白色矩形区域...")
|
||||
|
||||
# 检测第一张图片
|
||||
if img1_path.exists():
|
||||
print(f"\n检测图片: {img1_path}")
|
||||
result_img1, results1 = detect_white_rectangles_in_image(str(img1_path))
|
||||
|
||||
print("检测结果:")
|
||||
for result in results1:
|
||||
print(f" 类型: {result['type']}, 位置: {result['position']}, 置信度: {result['confidence']}")
|
||||
print(f" 特征: 白色比例={result['features']['white_ratio']:.2f}, "
|
||||
f"宽高比={result['features']['aspect_ratio']:.2f}")
|
||||
|
||||
# 保存结果图片
|
||||
output_path1 = img_dir / "result_1.jpg"
|
||||
cv2.imwrite(str(output_path1), result_img1)
|
||||
print(f"结果图片已保存到: {output_path1}")
|
||||
|
||||
# 检测第二张图片
|
||||
if img2_path.exists():
|
||||
print(f"\n检测图片: {img2_path}")
|
||||
result_img2, results2 = detect_white_rectangles_in_image(str(img2_path))
|
||||
|
||||
print("检测结果:")
|
||||
for result in results2:
|
||||
print(f" 类型: {result['type']}, 位置: {result['position']}, 置信度: {result['confidence']}")
|
||||
print(f" 特征: 白色比例={result['features']['white_ratio']:.2f}, "
|
||||
f"宽高比={result['features']['aspect_ratio']:.2f}")
|
||||
|
||||
# 保存结果图片
|
||||
output_path2 = img_dir / "result_2.jpg"
|
||||
cv2.imwrite(str(output_path2), result_img2)
|
||||
print(f"结果图片已保存到: {output_path2}")
|
||||
|
||||
print("\n检测完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
3
test/text_image.py
Normal file
3
test/text_image.py
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
|
||||
@ -118,58 +118,6 @@ class GestureControlNode(Node):
|
||||
elif msg.id == 7: # 停止手势
|
||||
self.get_logger().info("检测到停止手势")
|
||||
|
||||
def complete_loading(self):
|
||||
"""完成配货操作"""
|
||||
self.get_logger().info("配货完成,开始运输")
|
||||
|
||||
# 调用开始运输服务
|
||||
if self.start_transport_cli.wait_for_service(timeout_sec=1.0):
|
||||
req = Trigger.Request()
|
||||
future = self.start_transport_cli.call_async(req)
|
||||
future.add_done_callback(self.transport_start_callback)
|
||||
else:
|
||||
self.get_logger().warn("开始运输服务不可用")
|
||||
|
||||
# 更新状态
|
||||
self.current_state = "transporting"
|
||||
|
||||
def transport_start_callback(self, future):
|
||||
"""运输开始服务回调"""
|
||||
try:
|
||||
response = future.result()
|
||||
if response.success:
|
||||
self.get_logger().info("运输已开始")
|
||||
else:
|
||||
self.get_logger().warn("运输启动失败")
|
||||
except Exception as e:
|
||||
self.get_logger().error(f"运输服务调用失败: {e}")
|
||||
|
||||
def complete_unloading(self):
|
||||
"""完成卸货操作"""
|
||||
self.get_logger().info("卸货完成,准备新的配货")
|
||||
|
||||
# 调用完成卸货服务
|
||||
if self.complete_unloading_cli.wait_for_service(timeout_sec=1.0):
|
||||
req = Trigger.Request()
|
||||
future = self.complete_unloading_cli.call_async(req)
|
||||
future.add_done_callback(self.unloading_complete_callback)
|
||||
else:
|
||||
self.get_logger().warn("完成卸货服务不可用")
|
||||
|
||||
# 更新状态
|
||||
self.current_state = "waiting_for_loading"
|
||||
|
||||
def unloading_complete_callback(self, future):
|
||||
"""卸货完成服务回调"""
|
||||
try:
|
||||
response = future.result()
|
||||
if response.success:
|
||||
self.get_logger().info("卸货已完成确认")
|
||||
else:
|
||||
self.get_logger().warn("卸货完成确认失败")
|
||||
except Exception as e:
|
||||
self.get_logger().error(f"卸货完成服务调用失败: {e}")
|
||||
|
||||
def timer_callback(self):
|
||||
"""定时器回调,用于状态检查和超时处理"""
|
||||
# 检查手势识别是否激活且超时
|
||||
@ -181,7 +129,6 @@ class GestureControlNode(Node):
|
||||
# 这里可以添加状态机逻辑,根据实际需求更新current_state
|
||||
# 例如,当机器狗到达配货区域时,设置self.current_state = "loading"
|
||||
# 当机器狗到达卸货区域时,设置self.current_state = "unloading"
|
||||
|
||||
def update_state(self, new_state):
|
||||
"""更新机器狗状态"""
|
||||
old_state = self.current_state
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user