← Back to Projects

Challenge Cup (挑战杯)

Apr. 2023 – Sept. 2023  ·  Competition

Overview

The "Challenge Cup" (挑战杯) is China's premier science and technology competition for university students. The 2023 edition in Guizhou featured 21 topics and over 2,000 participating projects. Our team was awarded the Grand Prize (特等奖) — the highest honor in the competition.

Attending the competition

Highlights

Grand Prize
Highest Award
99%
Area Coverage
99%
Detection Rate
61 min
Avg. Task Time
8
Agents
31
Targets

Problem & Requirements

This project addresses the challenge of information gathering in unmanned maritime areas by presenting a multi-agent decision control model for collision-free area coverage and target exploration within a convex polygonal region:

  • Complete coverage of the entire area;
  • For each target, perform multiple scans at fixed angles;
  • Avoid restricted zones and prevent collisions between agents.

The final model was deployed to the organizer's simulation platform and validated using real ship dynamics. Results were evaluated on coverage ratio, collision avoidance failures, and total simulation time.

Team

HAO Pengkun Team Leader

Framework design, simulation environment, coverage path planning, multi-agent decision logic, OpenCV simulation, system integration.

WANG Yue

Collaborative exploration & task allocation algorithms, system integration.

HUANG Jingyi

Artificial potential fields & A* obstacle avoidance, utility functions (convex hull, shortest distance, etc.).

LIN Yuheng

Multi-agent collision avoidance based on RVO (Reciprocal Velocity Obstacles).

Additional Contributors

Built and debugged the cluster simulation platform — Potato.

Instructors: LI Xiaoduo, HAN Liang, REN Zhang

Our Approach

Our algorithm consists of four algorithmic modules executing subtasks and a cluster simulation platform for validation:

  • Obstacle avoidance / collision avoidance
  • Area coverage trajectory planning
  • Target exploration
  • Dynamic task allocation

The simulation platform is built using Python, the OpenCV engine, and the Cesium engine.

Framework of our projectSystem framework: four algorithmic modules + simulation platform.

Obstacle Avoidance

Throughout the competition, our obstacle avoidance strategy evolved through three stages:

Stage 1 — Artificial Potential Fields

In the initial stage, agents only needed to avoid targets. We used the artificial potential field method with a second-order dynamics model and PID control. For each agent, the need_doa method determines whether obstacle avoidance is required:

def need_doa(self):
    list_doa_agent = []
    for i in range(self.num_agents):
        for j in range(i + 1, self.num_agents):
            if self.agents[i].check_collision_agent(self.agents[j]):
                list_doa_agent.append([i, j])
    list_doa_target = []
    for i in range(self.num_agents):
        for target_idx in range(self.num_targets):
            distance = np.linalg.norm(
                np.array([self.agents[i].position])
                - np.array([self.targets[target_idx].coord])
            )
            if distance < self.obstacle_dist:
                if self.list_doa_target_key[target_idx] == 0:
                    self.list_doa_target_key[target_idx] = 1
                else:
                    list_doa_target.append([i, target_idx])
    if len(list_doa_agent) > 0 or len(list_doa_target):
        return True, list_doa_agent, list_doa_target
    else:
        return False, list_doa_agent, list_doa_target

The resultant force of attraction (toward destination) and repulsion (from targets within 500m) determines the next waypoint. A constant repulsive force is applied within 50m to prevent excessive deflection.

Stage 2 — Avoiding Restricted Zones

When organizers introduced convex polygonal avoidance zones, we identified the closest point on the polygon to the agent and treated it as an obstacle:

def distance_point_to_line(point, line_start, line_end):
    line_start = np.array(line_start)
    line_end = np.array(line_end)
    line_vec = line_end - line_start
    point_vec = point - line_start
    t = np.dot(point_vec, line_vec) / np.dot(line_vec, line_vec)
    t = max(0, min(1, t))
    closest_point = line_start + t * line_vec
    distance = np.linalg.norm(point - closest_point)
    return distance, closest_point

def shortest_distance_to_polygon(point, polygon_vertices):
    min_distance = float('inf')
    closest_point = None
    for i in range(len(polygon_vertices)):
        start_point = polygon_vertices[i]
        end_point = polygon_vertices[(i + 1) % len(polygon_vertices)]
        distance, closest = distance_point_to_line(point, start_point, end_point)
        if distance < min_distance:
            min_distance = distance
            closest_point = closest
    return min_distance, closest_point
Calculate U_rep from the convex hullRepulsive force calculation from the convex hull of the avoidance zone.

However, a convergence point issue was discovered: when both the agent and target lie within the perpendicular region of an avoidance zone edge, the potential field fails.

Convergent pointConvergence point issue: the agent gets trapped when both positions fall within the perpendicular region.

Stage 3 — A* Algorithm

To solve the convergence issue, we adopted the A* algorithm on the pre-gridified map for real-time collision-free path planning:

class AStarPathPlanner:
    def __init__(self, grid_map):
        self.grid_map = grid_map
        self.rows = len(grid_map)
        self.cols = len(grid_map[0])

    def heuristic(self, current, goal):
        return np.sqrt((current[0] - goal[0])**2 + (current[1] - goal[1])**2)

    def find_path(self, start, end):
        start = tuple(start)
        end = tuple(end)
        if start == end:
            return [[start[0], start[1]]]
        open_list = [(0, start)]
        came_from = {}
        g_score = {cell: float('inf') for row in self.grid_map for cell in row}
        g_score[start] = 0
        f_score = {cell: float('inf') for row in self.grid_map for cell in row}
        f_score[start] = self.heuristic(start, end)
        while open_list:
            _, current = heapq.heappop(open_list)
            if current == end:
                path = [current]
                while current in came_from:
                    current = came_from[current]
                    path.append(current)
                path.reverse()
                path_list = []
                for tpl in path:
                    path_list.append([tpl[0], tpl[1]])
                return path_list
            add = [(0,1),(0,-1),(1,0),(-1,0),(1,1),(1,-1),(-1,1),(-1,-1)]
            for dr, dc in add:
                neighbor = (current[0] + dr, current[1] + dc)
                if (0 <= neighbor[0] < self.rows and 0 <= neighbor[1] < self.cols
                        and self.grid_map[neighbor[0]][neighbor[1]] != 1):
                    tentative_g_score = g_score[current] + 1
                    if tentative_g_score < g_score.get(neighbor, float('inf')):
                        came_from[neighbor] = current
                        g_score[neighbor] = tentative_g_score
                        f_score[neighbor] = tentative_g_score + self.heuristic(neighbor, end)
                        heapq.heappush(open_list, (f_score[neighbor], neighbor))
        return []
A* algorithmA* path planning result on the gridified map.

Integrated Framework

The final need_doa logic checks whether the line segment between an agent's current position and its target intersects with any avoidance zone or target. If an intersection is detected, A* is used for avoidance zones and the artificial potential field for targets. The potential field is retained for its computational simplicity and to minimize code changes that could conflict with coverage and target-checking tasks.

Simulation Results

The following GIFs compare the algorithm performance before and after introducing online optimization:

Before optimizationBefore optimization — agents wander without coordination
After optimizationAfter optimization — coordinated coverage with task allocation

The GIF below demonstrates the coverage path planning before adding the target exploration module:

Coverage without target explorationCoverage path planning without target exploration

Final Video

The following video showcases our final simulation results in the organizer's system:

← Back to Projects