본문 바로가기
개발/기본) 알고리즘

알고리즘) PSO : Particle Swarm Optimization(입자 군집 최적화)

by 테샤르 2025. 5. 26.

PSO : Particle Swarm Optimization(입자 군집 최적화)

PSO는 Particle Swarm Optimization(입자 군집 최적화)이라는 최적화 알고리즘을 말한다.

1995년에 James Kennedy와 Russell Eberhart가 자연에서의 새 떼나 물고기 떼의 움직임에서 영감을 받아 만든 진화 계산 기법으로 많으면 군체의 움직임이 보여진다.

반응형

 

< 동작 원리 >

입자(Particle): 가능한 해(solution) 오브젝트
군집(Swarm): 여러 개의 입자로 구성된 집단
입자들은 공간 내에서 이동하면서 최적 해를 찾아 간다.
각 입자는 자신이 찾은 최적 해와, 전체 군집 중 가장 좋은 해를 참고하여 방향을 조정해서 이동하게 된다.

 

 

< 예시 코드 >

using System.Collections.Generic;
using UnityEngine;

public class PSOManager : MonoBehaviour
{
    [Header("PSO Settings")]
    public int swarmSize = 30;
    public float inertiaWeight = 0.5f;
    public float cognitiveWeight = 1.5f;
    public float socialWeight = 1.5f;
    public float maxSpeed = 0.3f;

    [Header("References")]
    public GameObject particlePrefab;
    public Vector2 targetPosition = new Vector2(-2f, 3f);
    public Transform[] obstacles; // optional: 장애물 회피 추가 가능

    private List<Particle> swarm;
    private Vector2 globalBestPos;
    private float globalBestFitness;

    void Start()
    {
        swarm = new List<Particle>();
        globalBestFitness = float.MaxValue;

        for (int i = 0; i < swarmSize; i++)
        {
            Vector2 startPos = new Vector2(Random.Range(-5f, 5f), Random.Range(-5f, 5f));
            GameObject go = Instantiate(particlePrefab, new Vector3(startPos.x, startPos.y, 0f), Quaternion.identity);
            var p = new Particle(startPos, go, targetPosition);
            swarm.Add(p);

            if (p.bestFitness < globalBestFitness)
            {
                globalBestFitness = p.bestFitness;
                globalBestPos = p.bestPosition;
            }
        }
    }

    void Update()
    {
        foreach (Particle p in swarm)
        {
            // 기본 PSO 계산
            Vector2 inertia = inertiaWeight * p.velocity;
            Vector2 cognitive = cognitiveWeight * Random.value * (p.bestPosition - p.position);
            Vector2 social = socialWeight * Random.value * (globalBestPos - p.position);

            Vector2 newVelocity = inertia + cognitive + social;

            // 장애물 회피 계산
            foreach (var obs in obstacles)
            {
                float dist = Vector2.Distance(p.position, obs.position);
                if (dist < 1.0f)
                {
                    Vector2 away = (p.position - (Vector2)obs.position).normalized;
                    newVelocity += away * 0.5f; // 회피 강도 증가 가능
                }
            }

            // 속도 제한
            newVelocity = Vector2.ClampMagnitude(newVelocity, maxSpeed);

            // 타겟 도달 시 정지
            if ((p.position - targetPosition).magnitude < 0.01f)
            {
                p.velocity = Vector2.zero;
            }
            else
            {
                p.velocity = newVelocity;
                p.position += newVelocity;
            }

            // 최적값, 시각 업데이트
            p.UpdatePersonalBest();
            if (p.bestFitness < globalBestFitness)
            {
                globalBestFitness = p.bestFitness;
                globalBestPos = p.bestPosition;
            }

            p.UpdateVisual();
        }


        Debug.Log("Best: " + globalBestFitness.ToString("F6") + " at " + globalBestPos);
    }
}using UnityEngine;

public class Particle
{
    public Vector2 position;
    public Vector2 velocity;
    public Vector2 bestPosition;
    public float bestFitness;
    public GameObject visual;

    private Vector2 target;

    public Particle(Vector2 startPos, GameObject go, Vector2 targetPos)
    {
        position = startPos;
        velocity = Random.insideUnitCircle * 0.1f;
        bestPosition = position;
        target = targetPos;
        bestFitness = Fitness(position);
        visual = go;
    }

    public float Fitness(Vector2 pos)
    {
        return (pos - target).sqrMagnitude; // (x−tx)² + (y−ty)²
    }

    public void UpdatePersonalBest()
    {
        float currentFitness = Fitness(position);
        if (currentFitness < bestFitness)
        {
            bestFitness = currentFitness;
            bestPosition = position;
        }
    }

    public void UpdateVisual()
    {
        visual.transform.position = new Vector3(position.x, position.y, visual.transform.position.z);

        if (Fitness(position) < 0.001f)
        {
            Renderer rend = visual.GetComponent<Renderer>();
            if (rend != null)
                rend.material.color = Color.green;

            var ps = visual.GetComponent<ParticleSystem>();
            if (ps && !ps.isPlaying)
                ps.Play();
        }
    }
}

 

 

★☆☆☆☆

 

반응형

댓글