InĀ [1]:
import numpy as np
from robosandbox.optimization.sweeper import ParameterSweeper

np.random.seed(42)

Function¶

InĀ [2]:
import robosandbox.models.DH.Generic as generic
from robosandbox.performance.workspace import WorkSpace


def Global_Index(alpha0, alpha1, method="invcondition", axes="all", **kwargs):
    """function to evaluate robot performance for given alpha values"""
    robot = generic.GenericFour(alpha=[alpha0, alpha1, 0, 0])
    ws = WorkSpace(robot=robot)
    G = ws.global_indice(
        initial_samples=3000,
        batch_ratio=0.1,
        error_tolerance_percentage=1e-3,
        method=method,
        axes=axes,
        max_samples=30000,
        is_normalized=kwargs.get("is_normalized", False),
    )
    return G
InĀ [4]:
import plotly.graph_objects as go


def plot_global_index_results(
    alpha_list_deg0,
    alpha_list_deg1,
    res_mat,
    plot_type="heatmap",
    method="invcondition",
    axes="all",
    isSave=False,
    step=15,
    isNormalized=False,
    colorscale="Viridis",
    title="",
):
    """
    Plot the effect of alpha on the global indices using Plotly.
    """
    G_mat = res_mat
    fontsize = 40

    # Create the appropriate figure based on plot_type
    if plot_type == "heatmap":
        fig = go.Figure(
            data=go.Heatmap(
                z=G_mat,
                x=alpha_list_deg0,
                y=alpha_list_deg1,
                colorscale=colorscale,
                colorbar=dict(
                    title=dict(text=title, font=dict(size=fontsize)),
                    tickfont=dict(size=fontsize),
                ),
            )
        )
        # Apply layout settings for heatmap
        fig.update_layout(
            xaxis_title="α₀ [deg]",
            yaxis_title="α₁ [deg]",
            autosize=True,
            height=800,
            width=900,
            xaxis_title_font=dict(size=fontsize),
            yaxis_title_font=dict(size=fontsize),
            xaxis=dict(tickfont=dict(size=fontsize), dtick=step * 2),
            yaxis=dict(tickfont=dict(size=fontsize), dtick=step * 2),
        )
    elif plot_type == "surface":
        fontsize = 40
        fig = go.Figure(
            data=go.Surface(
                z=G_mat,
                x=alpha_list_deg0,
                y=alpha_list_deg1,
                colorbar=dict(
                    title=dict(text=title, font=dict(size=fontsize)),
                    tickfont=dict(size=fontsize),
                ),
                colorscale=colorscale,
            )
        )
        # Apply proper 3D scene layout settings for surface plot
        fontsize = 18
        fig.update_layout(
            scene=dict(
                xaxis_title="α₀ [deg]",
                yaxis_title="α₁ [deg]",
                xaxis_title_font=dict(size=32),
                yaxis_title_font=dict(size=32),
                # zaxis_title=method,
                xaxis=dict(tickfont=dict(size=fontsize), dtick=step * 4),
                yaxis=dict(
                    # titlefont=dict(size=fontsize),
                    tickfont=dict(size=fontsize),
                    dtick=step * 4,
                ),
                # zaxis=dict(
                #     # titlefont=dict(size=fontsize),
                #     tickfont=dict(size=fontsize)
                # ),
                # Do not display z axis's tick and title
                zaxis=dict(showticklabels=False, title=""),
                camera=dict(eye=dict(x=-1.2, y=-1.2, z=1.65), up=dict(x=0, y=0, z=1)),
                # aspectratio=dict(x=1, y=1, z=0.8),
            ),
            autosize=False,
            height=800,
            width=900,
        )
    else:
        fig = go.Figure()  # Default empty figure if plot_type is not recognized

    # Save the figure if requested
    if isSave:
        import os

        os.makedirs("fig/two_alpha", exist_ok=True)
        fig.write_image(
            f"fig/two_alpha/{method}_{axes}_{plot_type}_normalised_{isNormalized}.pdf",
            scale=4,
            width=900,
            height=800,
        )
        # fig.write_html(
        #     f"fig/two_alpha/{method}_{axes}_{plot_type}_normalised_{isNormalized}.html"
        # )

    # Display the figure
    fig.show()

Sweeping for Global Inverse Condition Number¶

InĀ [5]:
sweeper = ParameterSweeper(objective_function=Global_Index)

# Define parameter ranges
step = 15
alpha0_list = np.deg2rad(np.arange(0, 181, step))
alpha1_list = np.deg2rad(np.arange(0, 181, step))
InĀ [11]:
method = "invcondition"
normalize_options = [False]  # [True, False]
axes_options = ["trans"]  # ["trans", "all"]
isRun = (
    False  # Set to False if you want to load saved data rather than running the sweep
)

for isNormalized in normalize_options:
    for axes in axes_options:
        filename = f"data/{method}_{axes}_normalized_{isNormalized}.npz"

        # Run the sweep or load data
        if not isRun:
            data = np.load(filename)
            results = data["results"]
            result_matrix = data["result_matrix"].T
        else:
            results, result_matrix = sweeper.sweep(
                param_dict={"alpha0": alpha0_list, "alpha1": alpha1_list},
                fixed_params={
                    "method": method,
                    "axes": axes,
                    "is_normalized": isNormalized,
                },
                save_intermediate=False,
                save_path=filename,
            )

        # Round the results based on accepted precision
        results = np.round(results, 3)
        result_matrix = np.round(result_matrix, 3)

        # Plot the results
        plot_global_index_results(
            alpha_list_deg0=alpha0_list * 180 / np.pi,
            alpha_list_deg1=alpha1_list * 180 / np.pi,
            res_mat=result_matrix,
            plot_type="heatmap",
            method=method,
            axes=axes,
            isSave=True,
            isNormalized=isNormalized,
            step=step,
            title="G<sub>k<sub>t</sub><sup>-1</sup></sub>",
        )
        plot_global_index_results(
            alpha_list_deg0=alpha0_list * 180 / np.pi,
            alpha_list_deg1=alpha1_list * 180 / np.pi,
            res_mat=result_matrix,
            plot_type="surface",
            method=method,
            axes=axes,
            isSave=True,
            isNormalized=isNormalized,
            step=step,
            title="G<sub>k<sub>t</sub><sup>-1</sup></sub>",
        )

        print(f"Completed: method={method}, axes={axes}, isNormalized={isNormalized}")
Completed: method=invcondition, axes=trans, isNormalized=False

Sweeping for Global Yoshikawa Manipulability¶

InĀ [Ā ]:
sweeper = ParameterSweeper(objective_function=Global_Index)

# Define parameter ranges
step = 15
alpha0_list = np.deg2rad(np.arange(0, 181, step))
alpha1_list = np.deg2rad(np.arange(0, 181, step))

method = "yoshikawa"
normalize_options = [False]  # [True, False]
axes_options = ["trans"]  # ["trans", "all"]
isRun = (
    False  # Set to False if you want to load saved data rather than running the sweep
)

for isNormalized in normalize_options:
    for axes in axes_options:
        filename = f"data/{method}_{axes}_normalized_{isNormalized}.npz"

        # Run the sweep or load data
        if not isRun:
            data = np.load(filename)
            results = data["results"]
            result_matrix = data["result_matrix"].T
        else:
            results, result_matrix = sweeper.sweep(
                param_dict={"alpha0": alpha0_list, "alpha1": alpha1_list},
                fixed_params={
                    "method": method,
                    "axes": axes,
                    "is_normalized": isNormalized,
                },
                save_intermediate=False,
                save_path=filename,
            )

        # Round the results based on accepted precision
        results = np.round(results, 3)
        result_matrix = np.round(result_matrix, 3)

        # Plot the results
        plot_global_index_results(
            alpha_list_deg0=alpha0_list * 180 / np.pi,
            alpha_list_deg1=alpha1_list * 180 / np.pi,
            res_mat=result_matrix,
            plot_type="heatmap",
            method=method,
            axes=axes,
            isSave=True,
            isNormalized=isNormalized,
            step=step,
            title="G<sub>&mu;<sub>t</sub></sub>",
        )
        plot_global_index_results(
            alpha_list_deg0=alpha0_list * 180 / np.pi,
            alpha_list_deg1=alpha1_list * 180 / np.pi,
            res_mat=result_matrix,
            plot_type="surface",
            method=method,
            axes=axes,
            isSave=True,
            isNormalized=isNormalized,
            step=step,
            title="G<sub>&mu;<sub>t</sub></sub>",
        )

        print(f"Completed: method={method}, axes={axes}, isNormalized={isNormalized}")
alpha0 progress: 100%|ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ| 13/13 [02:53<00:00, 13.38s/iter]
WARNING	Thread(Thread-14 (run)) choreographer.browser_async:browser_async.py:_close()- Resorting to unclean kill browser.
Completed: method=yoshikawa, axes=trans, isNormalized=False

Evaluate $\mu$ and $\mu_t$ on c1¶

InĀ [6]:
c1 = generic.Generic(
    dofs=4,
    a=[0, -0.4, -0.4, -0.4],
    d=[0.4, 0, 0, 0],
    alpha=[np.pi / 2, 0, 0, 0],
    qlim=[[-np.pi, np.pi]] * 4,
    offset=[0, -np.pi / 2, 0, 0],
)
fig = c1.plotly(q=c1.qz, zoom_factor=0.5)

$\mu_t$¶

InĀ [89]:
method = "yoshikawa"
axes = "trans"

c1_ws = WorkSpace(robot=c1)
G_mu_t = c1_ws.global_indice(
    initial_samples=3000,
    batch_ratio=0.1,
    error_tolerance_percentage=1e-2,
    method=method,
    axes=axes,
    max_samples=50000,
    is_normalized=False,  # Use the slider value here
)

fig = go.Figure()
fig = c1_ws.plot(color=method, fig=fig, isShow=False)
# set color bar range
fig.update_traces(
    marker_colorbar=dict(tickfont=dict(size=22), title="Ī¼ā‚œ"),
)
fig.update_layout(
    scene=dict(
        xaxis_title_font=dict(size=22),
        yaxis_title_font=dict(size=22),
        zaxis_title_font=dict(size=22),
        xaxis=dict(tickfont=dict(size=18)),
        yaxis=dict(tickfont=dict(size=18)),
        zaxis=dict(tickfont=dict(size=18)),
        camera=dict(
            eye=dict(
                x=1.6,
                y=-1.6,
                z=1,
            ),  # Position of the camera
            center=dict(x=0, y=0, z=0),  # Point the camera is looking at
            up=dict(x=0, y=0, z=1),  # Up vector direction
        ),
    ),
    width=800,
    height=600,
    margin=dict(l=0, r=0, b=0, t=0),
)

fig.show("png")
fig.write_image(f"./fig/workspace_{method}_{axes}.pdf", scale=4)
WARNING	Thread(Thread-289 (run)) choreographer.browser_async:browser_async.py:_close()- Resorting to unclean kill browser.
No description has been provided for this image
InĀ [87]:
print(f"Global translational manipulability of c1: {G_mu_t}")
Global translational manipulability of c1: 0.10550654203634664

$\mu$¶

InĀ [Ā ]:
method = "yoshikawa"
axes = "all"

c1_ws = WorkSpace(robot=c1)
G_mu = c1_ws.global_indice(
    initial_samples=3000,
    batch_ratio=0.1,
    error_tolerance_percentage=1e-2,
    method=method,
    axes=axes,
    max_samples=50000,
    is_normalized=False,  # Use the slider value here
)

fig = go.Figure()
fig = c1_ws.plot(color=method, fig=fig, isShow=False)
fig.update_traces(
    marker_colorbar=dict(tickfont=dict(size=22), title="μ"),
    marker=dict(cmin=0, cmax=0.001),
)


fig.update_layout(
    scene=dict(
        xaxis_title_font=dict(size=22),
        yaxis_title_font=dict(size=22),
        zaxis_title_font=dict(size=22),
        xaxis=dict(tickfont=dict(size=18)),
        yaxis=dict(tickfont=dict(size=18)),
        zaxis=dict(tickfont=dict(size=18)),
        camera=dict(
            eye=dict(
                x=1.6,
                y=-1.6,
                z=1,
            ),  # Position of the camera
            center=dict(x=0, y=0, z=0),  # Point the camera is looking at
            up=dict(x=0, y=0, z=1),  # Up vector direction
        ),
    ),
    width=800,
    height=600,
    margin=dict(l=0, r=0, b=0, t=0),
)

fig.show("png")
fig.write_image(f"./fig/workspace_{method}_{axes}.pdf", scale=4)
No description has been provided for this image
InĀ [85]:
print(f"Global manipulability of c1: {G_mu}")
Global manipulability of c1: 2.7393050132558384e-18