While both designed to contain files not belonging to the Operating System,
/usr/local are not designed to contain the same set of files.
/usr/local is a place to install files built by the administrator usually by using the
make command (eg:
./configure; make; make install). The idea is to avoid clashes with files that are part of the operating systems which would either be overwritten or overwrite the local ones otherwise.
/usr/bin/foo is part of the OS while
/usr/local/bin/foo is a local alternative.
All files under
/usr are shareable between OS instances although this is rarely done with Linux. This is a part where the FHS is weak, or at least self-contraditory, as
/usr is defined to be read-only but
/usr/local/bin needs to be read-write for local installation of software to succeed. The SVR4 file system standard which was the FHS main source of inspiration is recommending to avoid
/usr/local and use
/opt/local instead to avoid this issue.
/usr/local is a legacy from the original BSD. At that time, the source code of
/usr/bin OS commands were in
/usr/src/usr.bin while the source of commands developed locally was in
/usr/local/src and their binaries in
/usr/local/bin. There was no notion of packaging (outside tarballs).
On the other hand,
/opt is a directory where to install unbundled packages, each in its own subdirectory. They are already built whole packages provided by an independent third party software distributor. Unlike
/usr/local stuff, these package follow the directory conventions. For example
someapp would be installed in
/opt/someapp with one of its command being
/opt/someapp/bin/foo, its configuration file would be in
/etc/opt/someapp/foo.conf, and its log files in